La herencia virtual, que Laura sugirió, es, por supuesto, la solución del problema. Pero no termina teniendo solo una InterfaceA. También tiene "efectos secundarios", por ej. ver https://isocpp.org/wiki/faq/multiple-inheritance#mi-delegate-to-sister. Pero si te acostumbras, puede ser útil.
Si no desea que los efectos secundarios, es posible utilizar la plantilla:
struct InterfaceA
{
virtual void methodA() = 0;
};
template<class IA>
struct ClassA : public IA //IA is expected to extend InterfaceA
{
void methodA() { 5+1;}
};
struct InterfaceB : public InterfaceA
{
virtual void methodB() = 0;
};
struct ClassAB
: public ClassA<InterfaceB>
{
void methodB() {}
};
int main()
{
InterfaceB* test = new ClassAB();
test->methodA();
}
Por lo tanto, estamos teniendo exactamente una clase padre.
Pero parece más feo cuando hay más de una clase "compartida" (InterfaceA es "compartida", porque está encima de "temido diamante", ver aquí https://isocpp.org/wiki/faq/multiple-inheritance según lo publicado por Laura). Ver ejemplo (lo que será, si se implementa claseA InterfaceC también):
struct InterfaceC
{
virtual void methodC() = 0;
};
struct InterfaceD : public InterfaceC
{
virtual void methodD() = 0;
};
template<class IA, class IC>
struct ClassA
: public IA //IA is expected to extend InterfaceA
, public IC //IC is expected to extend InterfaceC
{
void methodA() { 5+1;}
void methodC() { 1+2; }
};
struct InterfaceB : public InterfaceA
{
virtual void methodB() = 0;
};
struct ClassAB
: public ClassA<InterfaceB, InterfaceC> //we had to modify existing ClassAB!
{
void methodB() {}
};
struct ClassBD //new class, which needs ClassA to implement InterfaceD partially
: public ClassA<InterfaceB, InterfaceD>
{
void methodB() {}
void methodD() {}
};
Lo malo, que se necesitaba para modificar ClassAB existente. Sin embargo, se puede escribir:
template<class IA, class IC = interfaceC>
struct ClassA
Entonces ClassAB permanece sin cambios:
struct ClassAB
: public ClassA<InterfaceB>
Y tiene aplicación por defecto para el parámetro de plantilla IC.
Qué manera de usar es para que usted decida. Prefiero la plantilla, cuando es simple de entender.Es muy difícil entrar en el hábito, que B :: incrementAndPrint() y C :: incrementAndPrint() se imprimirán valores diferentes (no tu ejemplo), ver esto:
class A
{
public:
void incrementAndPrint() { cout<<"A have "<<n<<endl; ++n; }
A() : n(0) {}
private:
int n;
};
class B
: public virtual A
{};
class C
: public virtual A
{};
class D
: public B
: public C
{
public:
void printContents()
{
B::incrementAndPrint();
C::incrementAndPrint();
}
};
int main()
{
D d;
d.printContents();
}
Y la salida:
A have 0
A have 1
Gracias por la aclaración de Laura. –
Pensé que una vez que se declara una función 'virtual' es siempre virtual en toda la jerarquía de clases, ya sea que las clases derivadas usen' virtual' o no al definirlo – johnbakers