Directorio de artículos
1 Problema de herencia múltiple 3: generar múltiples tablas de funciones virtuales
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:
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
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