475 votes

En C++, ce qui est une classe de base virtuelle ?

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

Permettez-moi de vous montrer un exemple :

594voto

OJ. Points 16939

Virtuel les classes de base, utilisé dans l'héritage virtuel, est une façon de prévenir plusieurs "instances" d'une classe donnée apparaissant dans une hiérarchie d'héritage lors de l'utilisation de l'héritage multiple.

Envisagez le scénario suivant:

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

La classe ci-dessus hiérarchie des résultats dans le "redouté de diamant" qui ressemble à ceci:

  A
 / \
B   C
 \ /
  D

Une instance de D sera composé de B, qui comprend Un, et C, qui comprend également A. Donc, vous avez deux "instances" (faute d'une meilleure expression) d'A.

Lorsque vous avez ce scénario, vous avez la possibilité d'ambiguïté. Ce qui se passe quand vous faites cela:

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. Lorsque vous spécifiez virtuel lorsque vous héritez de vos classes, vous avez dit 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'est qu'une "instance" d'Un inclus dans la hiérarchie. Donc

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.

302voto

paercebal Points 38526

Au sujet de la disposition de la mémoire

Comme une note côté, le problème avec les fameux Diamant est que la classe de base est présent plusieurs fois. Donc, avec régulièrement de l'héritage, vous croyez que vous avez:

  A
 / \
B   C
 \ /
  D

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

A   A
|   |
B   C
 \ /
  D

C'est pourquoi lors de l'appel d' D::foo(), vous avez un problème d'ambiguïté. Mais le réel problème vient quand vous voulez utiliser une variable de membre de l' A. Par exemple, disons que nous avons:

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

Lorsque vous essayez d'accéder m_iValue de D, le compilateur de protestation, parce que dans la hiérarchie, il va voir deux m_iValue, pas un seul. Et si vous modifiez l'un, disons, B::m_iValue (c'est - A::m_iValue parent d' B), C::m_iValue ne seront pas modifiés (c'est - A::m_iValue parent d' C).

C'est là l'héritage virtuel vient à portée de main, avec elle, vous obtiendrez de retour à un véritable diamant de mise en page, avec non seulement un foo() méthode, mais aussi la un et un seul m_iValue.

Ce qui pourrait aller mal?

Imaginez:

  • A a certaines caractéristiques de base.
  • B y ajoute une sorte de cool tableau de données (par exemple)
  • C y ajoute quelques frais de la fonctionnalité comme un observateur modèle (par exemple, sur m_iValue).
  • D hérite B et C, et donc de A.

Avec la normale de l'héritage, modifiant m_iValue de D est ambigu et cela doit être résolu. Même si elle est, il y a deux m_iValues à l'intérieur d' D, de sorte que vous feriez mieux de rappeler et de mettre à jour les deux en même temps.

Avec l'héritage virtuel, modifiant m_iValue de D est ok... Mais... disons que vous avez D. Par le biais de son C interface, vous joint un observateur. Et à travers ses B interface, vous mettez à jour les frais de tableau, avec a pour effet de bord de modifier directement m_iValue...

Comme le changement de m_iValue se fait directement (sans l'aide d'un virtuel méthode d'accesseur), l'observateur "à l'écoute" par C ne sera pas appelé, car le code de la mise en œuvre de l'écoute est en C, et B ne le savent pas...

Conclusion

Si vous rencontrez un diamant dans votre hiérarchie, cela signifie que vous avez 95% de l'avoir fait quelque chose de mal avec ceux de la hiérarchie.

38voto

lenkite Points 717

Expliquant multi-héritage virtuel bases nécessite une connaissance de l'objet C++ modèle. Et d'expliquer clairement l'objet est préférable de le faire dans un article et pas dans une zone de commentaire.

Le meilleur, lisible explication que j'ai trouvé qui a résolu tous mes doutes à ce sujet cet article: http://www.phpcompiler.org/articles/virtualinheritance.html

Vous aurez vraiment pas besoin de lire quoi que ce soit d'autre sur le sujet (sauf si vous êtes un compilateur écrivain) après la lecture de ce...

10voto

wilhelmtell Points 25504

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, serait ambigu, parfois, il reporte la fonction de gestion des appels pour une autre classe que ce qu'on pourrait attendre dans un non-héritage virtuel.

7voto

wilhelmtell Points 25504

Je voudrais ajouter à JO du genre de précisions.

L'héritage virtuel n'est pas sans un prix. Comme avec toutes les choses virtuel, vous obtenez un gain de performance. Il y a un moyen de contourner cette performance a frappé, c'est douteuse moins élégant.

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

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

Non de la classe hérite virtuall, héritent tous publicaly. Les Classes D21 et D22 se cacheront virtuel de la fonction f (), ce qui est ambigu pour les DD, peut-être, en déclarant la fonction privée. Chaque définir une fonction wrapper, f1() et f2() respectivement, chaque classe d'appel local (privé) f(), donc la résolution des conflits. Classe DD appels f1() si elle veut D11::f() et f2() si elle veut D12::f(). Si vous définissez les wrappers inline, vous aurez probablement à propos de zéro frais généraux.

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.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X