web-dev-qa-db-fra.com

Comment puis-je éviter le diamant de la mort lorsque j'utilise l'héritage multiple?

http://en.wikipedia.org/wiki/Diamond_problem

Je sais ce que cela signifie, mais quelles mesures puis-je prendre pour l'éviter?

53
ilitirit

Un exemple pratique:

class A {};
class B : public A {};
class C : public A {};
class D : public B, public C {};

Remarquez comment la classe D hérite des deux B et C. Mais les deux B et C héritent de A. Cela se traduira par 2 copies de la classe A étant incluses dans le vtable.

Pour résoudre ce problème, nous avons besoin d'un héritage virtuel. C'est la classe A qui doit être héritée virtuellement. Donc, cela résoudra le problème:

class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};
65
Mark Ingram

héritage virtuel. C'est pour ça qu'il est là.

14
eduffy

Je m'en tiendrai à l'utilisation de l'héritage multiple des interfaces uniquement. Bien que l'héritage multiple de classes soit parfois attrayant, il peut également être déroutant et douloureux si vous vous en remettez régulièrement.

12
Bob Somers

L'héritage est une arme forte et solide. Utilisez-le uniquement lorsque vous en avez vraiment besoin. Dans le passé, l'héritage diamant était un signe que j'allais trop loin avec la classification, disant qu'un utilisateur est un "employé" mais ils sont aussi un "écouteur de widgets", mais aussi un ...

Dans ces cas, il est facile de rencontrer plusieurs problèmes d'héritage.

Je les ai résolus en utilisant la composition et des pointeurs vers le propriétaire:

Avant:

class Employee : public WidgetListener, public LectureAttendee
{
public:
     Employee(int x, int y)
         WidgetListener(x), LectureAttendee(y)
     {}
};

Après:

class Employee
{
public:
     Employee(int x, int y)
         : listener(this, x), attendee(this, y)
     {}

     WidgetListener listener;
     LectureAttendee attendee;
};

Oui, les droits d'accès sont différents, mais si vous pouvez vous en sortir avec une telle approche, sans dupliquer le code, c'est mieux car c'est moins puissant. (Vous pouvez économiser de l'énergie lorsque vous n'avez pas d'alternative.)

6
anemptywhiteroom
class A {}; 
class B : public A {}; 
class C : public A {}; 
class D : public B, public C {};

En cela, les attributs de la classe A sont répétés deux fois dans la classe D, ce qui fait plus d'utilisation de la mémoire ... Donc, pour économiser de la mémoire, nous créons un attribut virtuel pour tous les attributs hérités de la classe A qui sont stockés dans une Vtable.

3
NItish

Eh bien, la grande chose au sujet du diamant redouté est que c'est une erreur quand elle se produit. La meilleure façon d'éviter est de déterminer à l'avance votre structure d'héritage. Par exemple, un projet sur lequel je travaille a des visionneuses et des éditeurs. Les éditeurs sont des sous-classes logiques des Viewers, mais comme tous les Viewers sont des sous-classes - TextViewer, ImageViewer, etc., Editor ne dérive pas de Viewer, permettant ainsi aux classes TextEditor et ImageEditor finales d'éviter le losange.

Dans les cas où le diamant n'est pas évitable, utilisez l'héritage virtuel. La plus grande mise en garde, cependant, avec les bases virtuelles, est que le constructeur de la base virtuelle doit soit appelé par la classe la plus dérivée, ce qui signifie qu'une classe qui en dérive n'a pratiquement aucun contrôle sur les paramètres du constructeur. De plus, la présence d'une base virtuelle a tendance à entraîner une pénalité performance/espace lors du lancement à travers la chaîne, bien que je ne pense pas qu'il y ait beaucoup de pénalité pour plus au-delà de la première.

De plus, vous pouvez toujours utiliser le diamant si vous êtes explicite sur la base que vous souhaitez utiliser. Parfois, c'est le seul moyen.

1
coppro

Utilisez l'héritage par délégation. Ensuite, les deux classes pointeront vers une base A, mais doivent implémenter des méthodes qui redirigent vers A. Cela a pour effet secondaire de transformer les membres protégés de A en membres "privés" en B, C et D, mais maintenant vous ne le faites pas besoin de virtuel, et vous n'avez pas de diamant.

0
Lee Louviere

Je proposerais une meilleure conception de classe. Je suis sûr que certains problèmes sont mieux résolus grâce à l'héritage multiple, mais vérifiez d'abord s'il y a une autre façon.

Sinon, utilisez des fonctions/interfaces virtuelles.

0
user17720