L'héritage virtuel, que Laura a suggéré, est, bien sûr, la solution du problème. Mais il ne finit pas avec une seule interfaceA. Il a aussi des "effets secondaires", par ex. voir https://isocpp.org/wiki/faq/multiple-inheritance#mi-delegate-to-sister. Mais si vous vous y êtes habitué, cela pourrait vous être utile.
Si vous ne voulez pas d'effets secondaires, vous pouvez utiliser le modèle:
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();
}
Ainsi, nous ayant exactement une classe parente.
Mais il semble plus laid quand il y a plus d'une classe "partagée" (InterfaceA est "partagé", parce qu'il est au-dessus de "diamant redouté", voir ici https://isocpp.org/wiki/faq/multiple-inheritance tel que posté par Laura). Voir par exemple (ce qui sera, si ClassA implémente InterfaceC aussi):
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() {}
};
La mauvaise chose, que vous aviez besoin de modifier ClassAB existant. Mais vous pouvez écrire:
template<class IA, class IC = interfaceC>
struct ClassA
Puis ClassAB reste inchangé:
struct ClassAB
: public ClassA<InterfaceB>
Et vous avez implémentation par défaut pour le paramètre de modèle IC.
De quelle manière vous pouvez décider. Je préfère le modèle, quand c'est simple à comprendre.Il est assez difficile d'entrer dans l'habitude, que B :: incrementAndPrint() et C :: incrementAndPrint() affichera différentes valeurs (pas votre exemple), voir ceci:
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();
}
Et la sortie:
A have 0
A have 1
Merci pour la clarification laura. –
Je pensais qu'une fois qu'une fonction est déclarée 'virtual', elle est toujours virtuelle dans toute la hiérarchie des classes, que les classes dérivées utilisent effectivement' virtual' ou non lors de la définition – johnbakers