Mehrfachvererbung in C++ ist mächtig, aber ein kniffliges Werkzeug, das oft zu Problemen führt, wenn es nicht sorgfältig verwendet wird – Probleme wie das Diamantproblem.
In diesem Artikel werden wir das Diamantproblem besprechen, wie es durch Mehrfachvererbung entsteht und was Sie tun können, um das Problem zu lösen.
Mehrfachvererbung in C++
Mehrfachvererbung ist a Funktion der objektorientierten Programmierung (OOP) wobei eine Unterklasse von mehr als einer Oberklasse erben kann. Mit anderen Worten, eine untergeordnete Klasse kann mehr als ein Elternteil haben.
Die folgende Abbildung zeigt eine bildliche Darstellung von Mehrfachvererbungen.
Im obigen Diagramm, Klasse C hat Klasse a und Klasse b als seine Eltern.
Betrachten wir ein reales Szenario, erbt ein Kind von seinem Vater und seiner Mutter. Ein Kind kann also als abgeleitete Klasse mit „Vater“ und „Mutter“ als Eltern dargestellt werden. In ähnlicher Weise können wir viele solcher realen Beispiele für Mehrfachvererbung haben.
Bei der Mehrfachvererbung werden die Konstruktoren einer geerbten Klasse in der Reihenfolge ihrer Vererbung ausgeführt. Auf der anderen Seite werden Destruktoren in umgekehrter Reihenfolge ihrer Vererbung ausgeführt.
Lassen Sie uns nun die Mehrfachvererbung veranschaulichen und die Reihenfolge der Konstruktion und Zerstörung von Objekten überprüfen.
Code-Illustration der Mehrfachvererbung
Für die Abbildung der Mehrfachvererbung haben wir die obige Darstellung exakt in C++ programmiert. Der Code für das Programm ist unten angegeben.
#enthalten
Verwenden des Namensraums std;
Klasse A //Basisklasse A mit Konstruktor und Destruktor
{
öffentlich:
A() { cout << "Klasse A:: Konstruktor" << endl; }
~A() { cout << "Klasse A:: Destruktor" << endl; }
};
Klasse B //Basisklasse B mit Konstruktor und Destruktor
{
öffentlich:
B() { cout << "Klasse B:: Konstruktor" << endl; }
~B() { cout << "Klasse B:: Destruktor" << endl; }
};
class C: public B, public A //abgeleitete Klasse C erbt Klasse A und dann Klasse B (Reihenfolge beachten)
{
öffentlich:
C() { cout << "Klasse C:: Konstruktor" << endl; }
~C() { cout << "Klasse C:: Destruktor" << endl; }
};
int main(){
Cc;
0 zurückgeben;
}
Die Ausgabe, die wir aus dem obigen Programm erhalten, ist wie folgt:
Klasse B:: Konstruktor
Klasse A:: Konstruktor
Klasse C:: Konstruktor
Klasse C:: Destruktor
Klasse A:: Destruktor
Klasse B:: Destruktor
Wenn wir nun die Ausgabe überprüfen, sehen wir, dass die Konstruktoren in der Reihenfolge B, A und C aufgerufen werden, während die Destruktoren in umgekehrter Reihenfolge sind. Nachdem wir nun die Grundlagen der Mehrfachvererbung kennen, besprechen wir das Diamantproblem.
Das Diamantproblem, erklärt
Das Diamantproblem tritt auf, wenn eine untergeordnete Klasse von zwei Elternklassen erbt, die beide eine gemeinsame Großelternklasse teilen. Dies ist im folgenden Diagramm dargestellt:
Hier haben wir eine Klasse Kind von Klassen erben Vater und Mutter. Diese beiden Klassen erben wiederum die Klasse Person weil sowohl Vater als auch Mutter Person sind.
Wie in der Abbildung gezeigt, erbt die Klasse Kind die Merkmale der Klasse Person zweimal – einmal vom Vater und erneut von der Mutter. Dies führt zu Mehrdeutigkeiten, da der Compiler nicht versteht, welchen Weg er einschlagen soll.
Dieses Szenario führt zu einem rautenförmigen Vererbungsgraphen und wird bekanntlich als "Das Diamantproblem" bezeichnet.
Code-Illustration des Diamantproblems
Unten haben wir das obige Beispiel einer rautenförmigen Vererbung programmatisch dargestellt. Der Code ist unten angegeben:
#enthalten
Verwenden des Namensraums std;
Klasse Person { //Klasse Person
öffentlich:
Person (int x) { cout << "Person:: Person (int) genannt" << endl; }
};
Klassenvater: öffentliche Person { //Klassenvater erbt Person
öffentlich:
Vater (int x):Person (x) {
cout << "Vater:: Vater (int) gerufen" << endl;
}
};
Klasse Mutter: öffentliche Person { //Klasse Mutter erbt Person
öffentlich:
Mutter (int x):Person (x) {
cout << "Mutter:: Mutter (int) gerufen" << endl;
}
};
Klasse Kind: öffentlich Vater, öffentlich Mutter { //Kind erbt Vater und Mutter
öffentlich:
Kind (int x):Mutter (x), Vater (x) {
cout << "Kind:: Kind (int) genannt" << endl;
}
};
int main() {
Kind Kind (30);
}
Nachfolgend die Ausgabe dieses Programms:
Person:: Person (int) angerufen
Vater:: Vater (int) angerufen
Person:: Person (int) angerufen
Mutter:: Mutter (int) angerufen
Kind:: Kind (int) genannt
Jetzt können Sie die Mehrdeutigkeit hier sehen. Der Konstruktor der Person-Klasse wird zweimal aufgerufen: einmal beim Erstellen des Vater-Klassenobjekts und dann beim Erstellen des Mutter-Klassenobjekts. Die Eigenschaften der Person-Klasse werden zweimal vererbt, was zu Mehrdeutigkeiten führt.
Da der Konstruktor der Klasse Person zweimal aufgerufen wird, wird der Destruktor auch zweimal aufgerufen, wenn das Objekt der Klasse Child zerstört wird.
Wenn Sie das Problem nun richtig verstanden haben, besprechen wir nun die Lösung des Diamantproblems.
So beheben Sie das Diamantproblem in C++
Die Lösung des Diamantproblems besteht darin, die virtuell Stichwort. Wir machen die beiden Elternklassen (die von derselben Großelternklasse erben) zu virtuellen Klassen, um zwei Kopien der Großelternklasse in der Kindklasse zu vermeiden.
Lassen Sie uns die obige Abbildung ändern und die Ausgabe überprüfen:
Code-Illustration zur Behebung des Diamantproblems
#enthalten
Verwenden des Namensraums std;
Klasse Person { //Klasse Person
öffentlich:
Person() { cout << "Person:: Person() aufgerufen" << endl; } //Basiskonstruktor
Person (int x) { cout << "Person:: Person (int) genannt" << endl; }
};
Klassenvater: virtuelle öffentliche Person { //Klassenvater erbt Person
öffentlich:
Vater (int x):Person (x) {
cout << "Vater:: Vater (int) gerufen" << endl;
}
};
Klasse Mutter: virtuelle öffentliche Person { //Klasse Mutter erbt Person
öffentlich:
Mutter (int x):Person (x) {
cout << "Mutter:: Mutter (int) gerufen" << endl;
}
};
class Child: public Father, public Mother { //class Child erbt Vater und Mutter
öffentlich:
Kind (int x):Mutter (x), Vater (x) {
cout << "Kind:: Kind (int) genannt" << endl;
}
};
int main() {
Kind Kind (30);
}
Hier haben wir die virtuell Schlüsselwort, wenn die Klassen Vater und Mutter die Klasse Person erben. Dies wird normalerweise als "virtuelle Vererbung" bezeichnet, die garantiert, dass nur eine einzige Instanz der geerbten Klasse (in diesem Fall der Klasse Person) weitergegeben wird.
Mit anderen Worten, die Child-Klasse hat eine einzelne Instanz der Person-Klasse, die sowohl von der Father- als auch der Mother-Klasse geteilt wird. Durch eine einzelne Instanz der Person-Klasse wird die Mehrdeutigkeit aufgelöst.
Die Ausgabe des obigen Codes ist unten angegeben:
Person:: Person() aufgerufen
Vater:: Vater (int) angerufen
Mutter:: Mutter (int) angerufen
Kind:: Kind (int) genannt
Hier sehen Sie, dass der Konstruktor der Klasse Person nur einmal aufgerufen wird.
Bei der virtuellen Vererbung ist zu beachten, dass selbst wenn der parametrisierte Konstruktor der Die Person-Klasse wird explizit von den Konstruktoren der Vater- und Mutterklasse durch Initialisierung aufgerufen Listen, nur der Basiskonstruktor der Person-Klasse wird aufgerufen.
Dies liegt daran, dass es nur eine einzige Instanz einer virtuellen Basisklasse gibt, die von mehreren Klassen geteilt wird, die davon erben.
Um zu verhindern, dass der Basiskonstruktor mehrmals ausgeführt wird, wird der Konstruktor für eine virtuelle Basisklasse nicht von der Klasse aufgerufen, die davon erbt. Stattdessen wird der Konstruktor vom Konstruktor der konkreten Klasse aufgerufen.
Im obigen Beispiel ruft die Klasse Child direkt den Basiskonstruktor für die Klasse Person auf.
Verwandt: Ein Leitfaden für Anfänger zur Standardvorlagenbibliothek in C++
Was ist, wenn Sie den parametrisierten Konstruktor der Basisklasse ausführen müssen? Sie können dies tun, indem Sie es explizit in der Child-Klasse statt in der Father- oder Mother-Klasse aufrufen.
Das Diamantproblem in C++, gelöst
Das Diamantproblem ist eine Mehrdeutigkeit, die bei Mehrfachvererbung auftritt, wenn zwei Elternklassen von derselben Großelternklasse erben und beide Elternklassen von einer einzigen Kindklasse geerbt werden. Ohne virtuelle Vererbung würde die Kindklasse die Eigenschaften der Großelternklasse zweimal erben, was zu Mehrdeutigkeiten führt.
Dies kann in realem Code häufig auftauchen, daher ist es wichtig, diese Mehrdeutigkeit zu beheben, wenn sie entdeckt wird.
Das Diamantproblem wird durch virtuelle Vererbung behoben, bei der die virtuell Das Schlüsselwort wird verwendet, wenn Elternklassen von einer gemeinsamen Großelternklasse erben. Dadurch wird nur eine Kopie der Großelternklasse erstellt, und die Objektkonstruktion der Großelternklasse wird von der Kindklasse durchgeführt.
Sie möchten Programmieren lernen, wissen aber nicht, wo Sie anfangen sollen? Diese Programmierprojekte und Tutorials für Anfänger werden Sie durchstarten.
Weiter lesen
- Programmierung
- C-Programmierung
Abonniere unseren Newsletter
Abonnieren Sie unseren Newsletter für technische Tipps, Rezensionen, kostenlose E-Books und exklusive Angebote!
Klicken Sie hier, um sich zu abonnieren