L'héritage multiple en C++ est puissant, mais un outil délicat, qui conduit souvent à des problèmes s'il n'est pas utilisé avec précaution, des problèmes comme le problème du diamant.

Dans cet article, nous discuterons du problème du diamant, de la manière dont il découle de l'héritage multiple et de ce que vous pouvez faire pour résoudre le problème.

Héritage multiple en C++

L'héritage multiple est un fonctionnalité de programmation orientée objet (POO) où une sous-classe peut hériter de plusieurs superclasses. En d'autres termes, une classe enfant peut avoir plusieurs parents.

La figure ci-dessous montre une représentation graphique des héritages multiples.

Dans le schéma ci-dessus, classe C a Classe A et classe B comme ses parents.

Si nous considérons un scénario de la vie réelle, un enfant hérite de son père et de sa mère. Ainsi, un enfant peut être représenté comme une classe dérivée avec « Père » et « Mère » comme parents. De même, nous pouvons avoir de nombreux exemples réels d'héritage multiple.

instagram viewer

Dans l'héritage multiple, les constructeurs d'une classe héritée sont exécutés dans l'ordre dans lequel ils sont hérités. D'autre part, les destructeurs sont exécutés dans l'ordre inverse de leur héritage.

Illustrons maintenant l'héritage multiple et vérifions l'ordre de construction et de destruction des objets.

Illustration de code d'héritage multiple

Pour l'illustration de l'héritage multiple, nous avons exactement programmé la représentation ci-dessus en C++. Le code du programme est donné ci-dessous.

#comprendre
en utilisant l'espace de noms std;
classe A //classe de base A avec constructeur et destructeur
{
Publique:
A() { cout << "classe A:: Constructeur" << endl; }
~A() { cout << "classe A:: Destructeur" << endl; }
};
classe B //classe de base B avec constructeur et destructeur
{
Publique:
B() { cout << "class B:: Constructor" << endl; }
~B() { cout << "class B:: Destructor" << endl; }
};
classe C: public B, public A //la classe dérivée C hérite de la classe A puis de la classe B (notez l'ordre)
{
Publique:
C() { cout << "class C:: Constructeur" << endl; }
~C() { cout << "class C:: Destructor" << endl; }
};
int main(){
Cc ;
renvoie 0 ;
}

La sortie que nous obtenons du programme ci-dessus est la suivante:

classe B:: Constructeur
classe A:: Constructeur
classe C:: Constructeur
classe C:: Destructeur
classe A:: Destructeur
classe B:: Destructeur

Maintenant, si nous vérifions la sortie, nous voyons que les constructeurs sont appelés dans l'ordre B, A et C tandis que les destructeurs sont dans l'ordre inverse. Maintenant que nous connaissons les bases de l'héritage multiple, nous passons au problème du diamant.

Le problème du diamant, expliqué

Le problème Diamond se produit lorsqu'une classe enfant hérite de deux classes parent qui partagent toutes deux une classe grand-parent commune. Ceci est illustré dans le schéma ci-dessous:

Ici, nous avons une classe Enfant hériter des classes Père et Mère. Ces deux classes héritent à leur tour de la classe Personne parce que le Père et la Mère sont la Personne.

Comme le montre la figure, la classe Enfant hérite deux fois des traits de la classe Personne: une fois de Père et une fois de Mère. Cela donne lieu à une ambiguïté car le compilateur ne parvient pas à comprendre la voie à suivre.

Ce scénario donne lieu à un graphique d'héritage en forme de losange et est connu sous le nom de « Le problème du diamant ».

Illustration du code du problème du diamant

Ci-dessous, nous avons représenté l'exemple ci-dessus d'héritage en forme de losange par programmation. Le code est donné ci-dessous:

#comprendre
en utilisant l'espace de noms std ;
class Personne { //class Personne
Publique:
Personne (int x) { cout << "Person:: Personne (int) appelée" << endl; }
};
class Père: public Person { //class Père hérite de Person
Publique:
Père (int x):Personne (x) {
cout << "Père:: Père (int) appelé" << endl;
}
};
class Mother: public Person { //class Mother hérite de Person
Publique:
Mère (int x):Personne (x) {
cout << "Mère:: Mère (int) appelée" << endl;
}
};
class Enfant: public Père, public Mère { // L'enfant hérite du Père et de la Mère
Publique:
Enfant (int x):Mère (x), Père (x) {
cout << "Enfant:: Enfant (int) appelé" << endl;
}
};
int main() {
Enfant enfant (30) ;
}

Voici le résultat de ce programme:

Personne:: Personne (int) appelée
Père:: Père (int) appelé
Personne:: Personne (int) appelée
Mère:: Mère (int) appelée
Enfant:: Enfant (int) appelé

Maintenant, vous pouvez voir l'ambiguïté ici. Le constructeur de classe Person est appelé deux fois: une fois lorsque l'objet de classe Père est créé et ensuite lorsque l'objet de classe Mère est créé. Les propriétés de la classe Person sont héritées deux fois, ce qui crée une ambiguïté.

Étant donné que le constructeur de classe Person est appelé deux fois, le destructeur sera également appelé deux fois lorsque l'objet de classe Child est détruit.

Maintenant, si vous avez bien compris le problème, discutons de la solution au problème du diamant.

Comment résoudre le problème de diamant en C++

La solution au problème du diamant est d'utiliser le virtuel mot-clé. Nous transformons les deux classes parent (qui héritent de la même classe grand-parent) en classes virtuelles afin d'éviter deux copies de la classe grand-parent dans la classe enfant.

Modifions l'illustration ci-dessus et vérifions la sortie:

Illustration de code pour résoudre le problème du diamant

#comprendre
en utilisant l'espace de noms std ;
class Personne { //class Personne
Publique:
Personne() { cout << "Personne:: Personne() appelée" << endl; } //Constructeur de base
Personne (int x) { cout << "Person:: Personne (int) appelée" << endl; }
};
class Father: virtual public Person { //class Father hérite de Person
Publique:
Père (int x):Personne (x) {
cout << "Père:: Père (int) appelé" << endl;
}
};
class Mother: virtual public Person { //class Mother hérite de Person
Publique:
Mère (int x):Personne (x) {
cout << "Mère:: Mère (int) appelée" << endl;
}
};
class Child: public Father, public Mother { //class L'enfant hérite du père et de la mère
Publique:
Enfant (int x):Mère (x), Père (x) {
cout << "Enfant:: Enfant (int) appelé" << endl;
}
};
int main() {
Enfant enfant (30) ;
}

Ici, nous avons utilisé le virtuel mot-clé lorsque les classes Père et Mère héritent de la classe Person. C'est ce qu'on appelle généralement « l'héritage virtuel », qui garantit qu'une seule instance de la classe héritée (dans ce cas, la classe Person) est transmise.

En d'autres termes, la classe Child aura une seule instance de la classe Person, partagée par les classes Father et Mother. En ayant une seule instance de la classe Person, l'ambiguïté est résolue.

La sortie du code ci-dessus est donnée ci-dessous:

Personne:: Personne() appelée
Père:: Père (int) appelé
Mère:: Mère (int) appelée
Enfant:: Enfant (int) appelé

Ici, vous pouvez voir que le constructeur de la classe Person n'est appelé qu'une seule fois.

Une chose à noter à propos de l'héritage virtuel est que même si le constructeur paramétré du La classe Person est explicitement appelée par les constructeurs de classes Father et Mother via l'initialisation listes, seul le constructeur de base de la classe Person sera appelé.

C'est parce qu'il n'y a qu'une seule instance d'une classe de base virtuelle qui est partagée par plusieurs classes qui en héritent.

Pour empêcher le constructeur de base de s'exécuter plusieurs fois, le constructeur d'une classe de base virtuelle n'est pas appelé par la classe qui en hérite. Au lieu de cela, le constructeur est appelé par le constructeur de la classe concrète.

Dans l'exemple ci-dessus, la classe Child appelle directement le constructeur de base de la classe Person.

En rapport: Guide du débutant sur la bibliothèque de modèles standard en C++

Que faire si vous devez exécuter le constructeur paramétré de la classe de base? Vous pouvez le faire en l'appelant explicitement dans la classe Child plutôt que dans les classes Father ou Mother.

Le problème du diamant en C++, résolu

Le problème du diamant est une ambiguïté qui survient dans l'héritage multiple lorsque deux classes parent héritent de la même classe grand-parent et que les deux classes parent sont héritées par une seule classe enfant. Sans utiliser l'héritage virtuel, la classe enfant hériterait deux fois des propriétés de la classe grand-parent, ce qui entraînerait une ambiguïté.

Cela peut survenir fréquemment dans le code du monde réel, il est donc important de résoudre cette ambiguïté chaque fois qu'elle est détectée.

Le problème du diamant est résolu en utilisant l'héritage virtuel, dans lequel le virtuel Le mot-clé est utilisé lorsque les classes parent héritent d'une classe grand-parent partagée. Ce faisant, une seule copie de la classe grand-parent est créée et la construction de l'objet de la classe grand-parent est effectuée par la classe enfant.

PartagerTweeterE-mail
Les 10 meilleurs projets pour débutants pour les nouveaux programmeurs

Vous voulez apprendre la programmation mais vous ne savez pas par où commencer? Ces projets et tutoriels de programmation pour débutants vous permettront de démarrer.

Lire la suite

Rubriques connexes
  • La programmation
  • C Programmation
A propos de l'auteur
Personnel MUO

Abonnez-vous à notre newsletter

Rejoignez notre newsletter pour des conseils techniques, des critiques, des ebooks gratuits et des offres exclusives !

Cliquez ici pour vous abonner