En C ++, qu’est-ce qu’une classe de base virtuelle?

Je veux savoir ce qu’est une ” classe de base virtuelle ” et ce que cela signifie.

Permettez-moi de montrer un exemple:

class Foo { public: void DoSomething() { /* ... */ } }; class Bar : public virtual Foo { public: void DoSpecific() { /* ... */ } }; 

    Les classes de base virtuelles, utilisées dans l’inheritance virtuel, permettent d’empêcher plusieurs «instances» d’une classe donnée d’apparaître dans une hiérarchie d’inheritance lors de l’utilisation de l’inheritance multiple.

    Considérez le scénario suivant:

     class A { public: void Foo() {} }; class B : public A {}; class C : public A {}; class D : public B, public C {}; 

    La hiérarchie de classes ci-dessus donne le “diamant redouté” qui ressemble à ceci:

      A / \ BC \ / D 

    Une instance de D sera composée de B, qui inclut A et C qui comprend également A. Vous avez donc deux “instances” (à défaut d’une meilleure expression) de A.

    Lorsque vous avez ce scénario, vous avez la possibilité d’une ambiguïté. Que se passe-t-il lorsque vous faites ceci:

     D d; d.Foo(); // is this B's Foo() or C's Foo() ?? 

    L’inheritance virtuel est là pour résoudre ce problème. Lorsque vous spécifiez virtual lors de l’inheritance de vos classes, vous indiquez au compilateur que vous ne souhaitez qu’une seule instance.

     class A { public: void Foo() {} }; class B : public virtual A {}; class C : public virtual A {}; class D : public B, public C {}; 

    Cela signifie qu’il n’y a qu’une seule “instance” de A incluse dans la hiérarchie. Par conséquent

     D d; d.Foo(); // no longer ambiguous 

    J’espère que ça aide comme mini résumé. Pour plus d’informations, lisez ceci et cela . Un bon exemple est également disponible ici .

    A propos de la disposition de la mémoire

    En outre, le problème avec le diamant redouté est que la classe de base est présente plusieurs fois. Donc, avec l’inheritance régulier, vous pensez avoir:

      A / \ BC \ / D 

    Mais dans la disposition de la mémoire, vous avez:

     AA | | BC \ / D 

    Cela explique pourquoi lorsque vous appelez D::foo() , vous avez un problème d’ambiguïté. Mais le vrai problème vient quand vous voulez utiliser une variable membre de A Par exemple, supposons que nous ayons:

     class A { public : foo() ; int m_iValue ; } ; 

    Lorsque vous essayez d’accéder à m_iValue partir de D , le compilateur va protester, car dans la hiérarchie, il verra deux m_iValue , pas un. Et si vous en modifiez un, par exemple B::m_iValue (c’est-à-dire le parent A::m_iValue de B ), C::m_iValue ne sera pas modifié (c’est-à-dire le parent A::m_iValue de C ).

    C’est là que l’inheritance virtuel devient pratique, comme avec lui, vous reviendrez à une véritable disposition en losange, avec non seulement une méthode foo() seulement, mais aussi une et une seule m_iValue .

    Qu’est-ce qui pourrait mal se passer?

    Imaginer:

    • A a une fonctionnalité de base.
    • B ajoute une sorte de tableau de données sympa (par exemple)
    • C ajoute à cela des fonctionnalités intéressantes comme un motif d’observateur (par exemple, sur m_iValue ).
    • D hérite de B et C , et donc de A

    Avec l’inheritance normal, modifier m_iValue partir de D est ambigu et cela doit être résolu. Même si c’est le cas, il y a deux m_iValues dans D , vous devriez donc vous en souvenir et mettre à jour les deux en même temps.

    Avec l’inheritance virtuel, modifier m_iValue partir de D est correct … Mais … Disons que vous avez D Grâce à son interface C , vous avez attaché un observateur. Et grâce à son interface B , vous mettez à jour le tableau cool, avec pour effet secondaire de changer directement m_iValue

    Comme le changement de m_iValue se fait directement (sans passer par une méthode d’accesseur virtuel), l’observateur «écoutant» par C ne sera pas appelé, car le code mettant en œuvre l’écoute est en C , et B ne le sait pas. .

    Conclusion

    Si vous avez un diamant dans votre hiérarchie, cela signifie que vous avez 95% à faire quelque chose de mal avec cette hiérarchie.

    Expliquer l’inheritance multiple avec des bases virtuelles nécessite une connaissance du modèle d’object C ++. Et mieux expliquer le sujet dans un article et non dans une boîte de commentaires.

    La meilleure explication lisible que j’ai trouvée qui a résolu tous mes doutes à ce sujet était cet article: http://www.phpcomstackr.org/articles/virtualinheritance.html

    Vous n’aurez vraiment pas besoin de lire autre chose sur le sujet (sauf si vous êtes un auteur de compilateurs) après avoir lu cela …

    Une classe de base virtuelle est une classe qui ne peut pas être instanciée: vous ne pouvez pas en créer d’object direct.

    Je pense que vous confondez deux choses très différentes. L’inheritance virtuel n’est pas la même chose qu’une classe abstraite. L’inheritance virtuel modifie le comportement des appels de fonctions. Parfois, il résout les appels de fonctions qui seraient autrement ambigus, parfois il reporte le traitement des appels de fonctions à une classe autre que celle à laquelle on s’attendrait dans un inheritance non virtuel.

    J’aimerais append aux précieuses explications de JO.

    L’inheritance virtuel ne va pas sans prix. Comme avec tout ce qui est virtuel, vous obtenez un coup de performance. Il y a un moyen de contourner ce problème de performance qui est peut-être moins élégant.

    Au lieu de briser le diamant en dérivant virtuellement, vous pouvez append une autre couche au diamant pour obtenir quelque chose comme ceci:

      B / \ D11 D12 | | D21 D22 \ / DD 

    Aucune des classes n’hérite virtuellement, toutes héritent publiquement. Les classes D21 et D22 masqueront alors la fonction virtuelle f () qui est ambiguë pour DD, peut-être en déclarant la fonction private. Ils définiraient chacun une fonction wrapper, respectivement f1 () et f2 (), appelant chaque classe local (privé) f (), résolvant ainsi les conflits. La classe DD appelle f1 () si elle veut D11 :: f () et f2 () si elle veut D12 :: f (). Si vous définissez les wrappers en ligne, vous aurez probablement à peu près zéro.

    Bien sûr, si vous pouvez changer D11 et D12, vous pouvez faire le même tour dans ces classes, mais ce n’est pas toujours le cas.

    En plus de ce qui a déjà été dit à propos des inheritances multiples et virtuels, il existe un article très intéressant dans le Journal du Dr Dobb: L’ inheritance multiple considéré comme utile

    Vous êtes un peu déroutant. Je ne sais pas si vous mélangez certains concepts.

    Vous n’avez pas de classe de base virtuelle dans votre OP. Vous avez juste une classe de base.

    Vous avez fait un inheritance virtuel. Ceci est généralement utilisé dans l’inheritance multiple, de sorte que plusieurs classes dérivées utilisent les membres de la classe de base sans les reproduire.

    Une classe de base avec une fonction virtuelle pure n’est pas instanciée. Cela nécessite la syntaxe que Paul obtient. Il est généralement utilisé pour que les classes dérivées définissent ces fonctions.

    Je ne veux pas en dire plus car je ne comprends pas tout à fait ce que vous demandez.

    Cela signifie qu’un appel à une fonction virtuelle sera transmis à la “bonne” classe.

    FAQ C ++ Lite FTW.

    En bref, il est souvent utilisé dans des scénarios à inheritance multiple, dans lesquels une hiérarchie «en losange» est formée. L’inheritance virtuel va alors briser l’ambiguïté créée dans la classe inférieure, lorsque vous appelez la fonction dans cette classe et que la fonction doit être résolue en classe D1 ou D2 au-dessus de cette classe inférieure. Voir l’ article FAQ pour un diagramme et des détails.

    Il est également utilisé dans la délégation soeur , une fonctionnalité puissante (mais pas pour les faibles de cœur). Voir cette FAQ.

    Voir également l’article 40 de Effective C ++ 3rd edition (43 en 2ème édition).

    Exemple d’utilisation de l’inheritance diamantaire

    Cet exemple montre comment utiliser une classe de base virtuelle dans le scénario standard: résoudre l’inheritance diamant.

     #include  class A { public: A(){} A(int i) : i(i) {} int i; virtual int f() = 0; virtual int g() = 0; virtual int h() = 0; }; class B : public virtual A { public: B(int j) : j(j) {} int j; virtual int f() { return this->i + this->j; } }; class C : public virtual A { public: C(int k) : k(k) {} int k; virtual int g() { return this->i + this->k; } }; class D : public B, public C { public: D(int i, int j, int k) : A(i), B(j), C(k) {} virtual int h() { return this->i + this->j + this->k; } }; int main() { D d = D(1, 2, 4); assert(df() == 3); assert(dg() == 5); assert(dh() == 7); } 

    Les classes virtuelles ne sont pas les mêmes que l’inheritance virtuel. Classes virtuelles que vous ne pouvez pas instancier, l’inheritance virtuel est tout à fait autre chose.

    Wikipedia le décrit mieux que moi. http://en.wikipedia.org/wiki/Virtual_inheritance