[Análisis en profundidad de C ++] 39. Herencia múltiple abandonada (abajo)

1 Problema de herencia múltiple 3: generar múltiples tablas de funciones virtuales

Inserte la descripción de la imagen aquí
La herencia es la superposición de la clase principal y la clase secundaria. Tanto BaseA como BaseB tienen funciones virtuales, por lo que ambas tendrán tablas de funciones virtuales. Dervied hereda estas dos clases, y hay dos punteros en la clase, que apuntan a dos. Tabla de funciones virtuales.

Experimento de programación: múltiples problemas de herencia que generan múltiples tablas de funciones virtuales

// 38-3.cpp
#include<iostream>
using namespace std;
class BaseA
{
public:
    virtual void funcA()
    {
        cout << "BaseA::funcA()" << endl;
    }
};
class BaseB
{
public:
    virtual void funcB()
    {
        cout << "BaseB::funcB()" << endl;
    }
};
class Derived : public BaseA, public BaseB
{
};
int main()
{
    Derived d;
    BaseA* pa = &d;
    BaseB* pb = &d;
    BaseB* pbb = (BaseB*)pa;					// 强制类型转换 BaseA*-->BaseB*
    BaseB* pbc = dynamic_cast<BaseB*>(pa);		// dynamic_cast类型转换
    cout << "sizeof(b) = " << sizeof(d) << endl;
    pa->funcA();
    pb->funcB();
    pbb->funcB();
    cout << endl;
    cout << "pa = " << pa << endl;
    cout << "pb = " << pb << endl;
    cout << "pbb = " << pbb << endl;
    cout << "pbc = " << pbc << endl;
    return 0;
}
  • Hay funciones virtuales en BaseA y BaseB. Derivado hereda estas dos clases, por lo que hay dos tablas de funciones virtuales y dos punteros a estas dos tablas de funciones virtuales. Entonces el tamaño derivado es 16.
  • La referencia de la subclase se puede convertir al puntero de la clase padre. Según el polimorfismo, pa puede llamar a la función funcA () en la clase BaseA, pb puede llamar a la función funcB () en la clase BaseB
  • En la línea 28, ¿qué función se llamará para convertir un puntero de tipo BaseA * en un puntero de tipo BaseB *?
  • Intente usar el nuevo tipo de conversión dynamic_cast, y finalmente imprimiremos los valores de los cuatro punteros.

Compilar y ejecutar:

$ g++ 38-3.cpp -o 38-3
$ ./38-3
sizeof(b) = 16
BaseA::funcA()
BaseB::funcB()
BaseA::funcA()

pa = 0x7ffc0d30ac40
pb = 0x7ffc0d30ac48
pbb = 0x7ffc0d30ac40
pbc = 0x7ffc0d30ac48

Se puede ver en los resultados:

  • El tamaño de la clase es 16, lo que indica que la subclase tiene dos tablas de funciones virtuales y dos punteros.
  • pbb es un puntero de tipo BaseB *, pero la llamada es de hecho una función en la clase BaseA. Puede ver desde el último valor de puntero impreso que pa, pbb todos apuntan a la clase BaseA, y pb y pbc apuntan a la clase BaseB.

¿Por qué el puntero pbb de tipo BaseB * apunta a la clase BaseA? Como se muestra en la figura a continuación:
Inserte la descripción de la imagen aquí
Conversión de tipo obligatoria directa, convirtiendo un puntero de tipo BaseA * en un puntero de tipo BaseB *, aún apuntando a la función miembro de BaseA. No puede llamar a las funciones miembro de BaseB.

1.1 Solución: Dynamic_cast

Para completar la conversión del tipo de puntero y cambiar la función apuntada al mismo tiempo, debe usar dynamic_cast para ayudarnos automáticamente a completar la conversión

Conversión de tipo Dynamic_cast

  • Se usa para convertir entre punteros de clase heredados
  • Se usa para la conversión entre punteros de clase con relaciones cruzadas
  • Con función de verificación de tipo
  • Necesita soporte de funciones virtuales

2 Uso adecuado de herencia múltiple

Herencia única de una clase + implementación de interfaces (múltiples)

Como se muestra en la siguiente figura: La
Inserte la descripción de la imagen aquí
herencia única de una implementación de clase + de (múltiples) interfaces evita la confusión de la herencia múltiple, lo que evita el problema de la redundancia de datos. Al mismo tiempo, el uso de dynamic_cast puede evitar los problemas causados ​​por la conversión del puntero y garantizar que cuando un puntero principal se convierte en otro puntero principal, la función llamada también cambiará en consecuencia.

// 38-4.cpp
#include<iostream>
using namespace std;
class Base
{
protected:
    int mi;
public:
    Base(int i) { mi = i; }
    int getI()  { return mi; }
    bool equal(Base* obj)
    {
        return this == obj;
    }
};

class Interface1
{
public:
    virtual void add(int i) = 0;
    virtual void minus(int i) = 0;
};
class Interface2
{
public:
    virtual void multiply(int i) = 0;
    virtual void divide(int i) = 0;
};
class Derived : public Base, public Interface1, public Interface2
{
public:
    Derived(int i) : Base(i) {}
    void add(int i)
    {
        mi += i;
    }
    void minus(int i)
    {
        mi -= i;
    }
    void multiply(int i)
    {
        mi *= i;
    }
    void divide(int i)
    {
        if (i != 0)
        {
            mi /= i;
        }
    }
};
int main()
{
    Derived d(100);
    Derived* p = &d;
    Interface1* pInt1 = &d;
    Interface2* pInt2 = &d;
    cout << "p->getI() = " << p->getI() << endl;
    pInt1->add(10);
    pInt2->divide(11);
    pInt1->minus(5);
    pInt2->multiply(8);
    cout << "p->getI() = " << p->getI() << endl;
    cout << endl;
    cout << "pInt1 == p : " << p->equal(dynamic_cast<Base*>(pInt1)) << endl;
    cout << "pInt1 == p : " << p->equal(dynamic_cast<Base*>(pInt2)) << endl;
    return 0;
}
  • Derivado tiene tres clases principales, una de las cuales es la clase Base y las otras dos son interfaces, lo que evita el problema de la redundancia de datos.
  • En las subclases, implemente equal () para determinar si el puntero apunta al objeto actual.
  • Convierta un puntero primario a otro puntero primario, use dynamic_cast
$ g++  38-4.cpp -o 38-4
$ ./38-4
p->getI() = 100
p->getI() = 40

pInt1 == p : 1
pInt1 == p : 1

Sugerencias de ingeniería:

  • Primero herede una clase, y luego implemente múltiples interfaces
  • Proporcione la función miembro igual () en la clase principal para determinar si el puntero apunta al objeto actual
  • La conversión de tipo obligatoria relacionada con la herencia múltiple se realiza con dynamic_cast

3 Resumen

1. Pueden aparecer
múltiples punteros de tabla de función virtual en herencia múltiple 2. La conversión de tipo obligatoria relacionada con herencia múltiple se completa con dynamic_cast
3. El proyecto usa "herencia única e interfaces múltiples" para usar herencia múltiple
4. La clase padre proporciona funciones de miembro para juzgar Si el puntero apunta al objeto actual

Publicado 298 artículos originales · elogiado 181 · 100,000+ vistas

Supongo que te gusta

Origin blog.csdn.net/happyjacob/article/details/104465822
Recomendado
Clasificación