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

je veux savoir ce qu'est une " classe de base virtuelle " et ce qu'elle signifie.

laissez-moi vous montrer un exemple:

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

class Bar : public virtual Foo
{
public:
    void DoSpecific() { /* ... */ }
};
345
demandé sur manlio 2008-08-22 05:13:15

10 réponses

classes de base virtuelles, utilisées dans l'héritage virtuel, est un moyen d'empêcher les "instances" multiples d'une classe donnée d'apparaître dans une hiérarchie d'héritage lors de l'utilisation de l'héritage multiple.

envisager 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 classe ci-dessus donne le "diamant redouté" qui ressemble à ceci:

  A
 / \
B   C
 \ /
  D

une instance de D sera composée de B, qui comprend A, et c qui comprend aussi inclut A. Donc vous avez deux "instances" (à défaut d'une meilleure expression) de A.

Quand vous avez ce scénario, vous avez la possibilité d'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'héritage Virtuel est là pour résoudre ce problème. Quand vous spécifiez virtuel lorsque vous héritez de vos classes, vous dites au compilateur que vous ne voulez 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 existe une seule "instance" d'Un inclus dans la hiérarchie. D'où

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

Hope qui aide comme un mini résumé. Pour plus d'informations, lire des ce et ce . Un bon exemple est également disponible ici .

484
répondu OJ. 2015-02-23 11:58:15

à Propos de la disposition de la mémoire

comme note latérale, le problème avec le diamant redouté est que la classe de base est présente plusieurs fois. Donc avec l'héritage régulier, vous croyez que vous avez:

  A
 / \
B   C
 \ /
  D

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

A   A
|   |
B   C
 \ /
  D

cela explique pourquoi lorsque vous appelez D::foo() , vous avez un problème d'ambiguïté. Mais le réel problème vient quand vous voulez utiliser un variable membre A . Par exemple, disons que nous avons:

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

quand vous allez essayer d'accéder à m_iValue de D , le compilateur va protester, parce que dans la hiérarchie, il verra deux m_iValue , pas un. Et si vous en modifiez un, dites B::m_iValue (c'est-à-dire le A::m_iValue parent de B ), C::m_iValue ne sera pas modifié (c'est-à-dire le A::m_iValue parent de C ).

c'est là que le virtuel l'héritage est pratique, comme avec elle, vous reviendrez à une véritable disposition de diamant, avec non seulement une méthode foo() seulement, mais aussi un et un seul m_iValue .

Qu'est-ce qui pourrait mal tourner?

Imaginez:

  • A a certaines caractéristiques de base.
  • B y ajoute une sorte de tableau de données cool (par exemple)
  • C y ajoute un peu de fraîcheur caractéristique comme un motif d'observateur (par exemple, sur m_iValue ).
  • D hérite de B et C , et donc de A .

avec héritage normal, modifier m_iValue de D est ambigu et cela doit être résolu. Même si c'est le cas, il y a deux m_iValues à l'intérieur de D , donc vous feriez mieux de vous en souvenir et de mettre à jour les deux en même temps.

Avec l'héritage virtuel, modifier m_iValue de D est ok... 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, qui a l'effet secondaire de changer directement m_iValue ...

comme le changement de m_iValue est fait directement (sans utiliser une méthode d'accesseur virtuel), l'observateur "écoutant" à travers C ne sera pas appelé, parce que le code mettant en œuvre l'écoute est dans C , et B ne sait pas à ce sujet...

Conclusion

si vous avez un diamant dans votre hiérarchie, cela signifie que vous avez 95% à avoir fait quelque chose de mal avec ladite hiérarchie.

216
répondu paercebal 2018-09-11 01:03:29

expliquer l'héritage multiple avec des bases virtuelles nécessite une connaissance du modèle d'objet C++. Et expliquer clairement le sujet est mieux fait dans un article et non dans une boîte de commentaire.

la meilleure explication lisible que j'ai trouvé qui a résolu tous mes doutes sur ce sujet était cet article: http://www.phpcompiler.org/articles/virtualinheritance.html

vous n'aurez vraiment pas besoin de lire quoi que ce soit d'autre sur le sujet (sauf si vous êtes un rédacteur de compilateur) après avoir lu cela...

29
répondu lenkite 2016-07-14 18:14:56

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

je pense que vous confondez deux choses très différentes. L'héritage virtuel n'est pas la même chose qu'une classe abstraite. L'héritage virtuel modifie le comportement des appels de fonction; parfois il résout les appels de fonction qui autrement seraient Ambigus, parfois il reporte le traitement des appels de fonction à une classe autre que c'est normal dans un héritage non virtuel.

10
répondu wilhelmtell 2008-08-22 01:47:36

je voudrais ajouter aux aimables clarifications D'OJ.

l'héritage virtuel n'a pas de prix. Comme avec tout ce qui est virtuel, vous obtenez un succès de performance. Il y a un moyen de contourner ce succès de performance qui est peut-être moins élégant.

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

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

aucune des classes n'hérite virtuellement, tous héritent publiquement. Les Classes D21 et D22 masquent alors la fonction virtuelle f() qui est ambiguë pour DD, peut-être en déclarant la fonction privée. Elles définiraient chacune une fonction d'enrubannage, f1() et f2() respectivement, chaque appel de 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 obtiendrez probablement environ zéro overhead.

bien sûr, si vous pouvez changer D11 et D12, alors vous pouvez faire le même truc à l'intérieur de ces classes, mais souvent ce n'est pas le cas.

6
répondu wilhelmtell 2017-04-13 08:13:15

en plus de ce qui a déjà été dit sur l'héritage(s) multiple (s) et virtuel (s), il y a un article très intéressant sur le Journal du Dr Dobb: L'héritage Multiple considéré comme utile

4
répondu Luc Hermitte 2008-09-22 00:58:41

Vous êtes un peu confus. Je ne sais pas si tu mélanges des concepts.

vous n'avez pas de classe de base virtuelle dans votre opération. Tu as juste un cours de base.

vous avez fait l'héritage virtuel. Ceci est habituellement utilisé dans l'héritage 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. ce il exige la syntaxe que Paul obtient. Il est généralement utilisé de sorte que les classes dérivées doivent définir ces fonctions.

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

1
répondu Baltimark 2008-08-22 01:48:04

signifie qu'un appel à une fonction virtuelle sera transmis à la classe" right".

C++ FAQ Lite FTW.

en bref, il est souvent utilisé dans les scénarios d'héritage multiple, où une hiérarchie" diamant " est formée. L'héritage virtuel rompra alors l'ambiguïté créée dans la classe inférieure, quand vous appelez la fonction dans cette classe et la fonction doit être résolue à la classe D1 ou D2 au-dessus de cette classe inférieure. Voir la rubrique FAQ pour un schéma et des détails.

Il est également utilisé dans "1519110920 sœur" délégation , une fonctionnalité puissante (mais pas pour les faibles de cœur). Voir this FAQ.

voir Aussi l'Article 40 Effective C++ 3e édition (43 2e édition).

1
répondu wilhelmtell 2015-02-23 11:58:23

Diamant héritage praticable exemple d'utilisation

Cet exemple montre comment utiliser une classe de base virtuelle dans le scénario typique: pour résoudre diamant héritage.

#include <cassert>

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(d.f() == 3);
    assert(d.g() == 5);
    assert(d.h() == 7);
}
1

les classes virtuelles sont et non comme l'héritage virtuel. Classes virtuelles que vous ne pouvez pas instancier, l'héritage virtuel est quelque chose d'autre entièrement.

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

0
répondu bradtgmurray 2008-08-22 01:52:04