Mehrfachvererbung ermöglicht es einer Klasse, die Schnittstelle und Implementierung von mehr als einer Basisklasse zu erben. Dies ist eine mächtige Eigenschaft von C++, die seit den frühen Tagen der Sprache häufig verwendet wurde, um komplexe Architekturen zu implementieren.
Historie des Themas: Die Mehrfachvererbung wurde in C++ als Mittel zur Erstellung wiederverwendbarer Komponenten und zur Kombination verschiedener Rollen in einer Klasse eingeführt (z.B. wenn ein Objekt gleichzeitig ein Thread und eine Queue ist).
Problem: Die Hauptschwierigkeiten bei der Mehrfachvererbung sind das 'Diamond Problem' (Problem der rhombischen Vererbung), die Mehrdeutigkeit beim Zugriff auf Mitglieder der Basisklassen und die Unklarheit der Aufrufreihenfolge von Konstruktoren/Destruktoren.
Lösung: Um die genannten Probleme zu vermeiden, wird in C++ virtuelle Vererbung vorgesehen. Dies stellt sicher, dass ein gemeinsamer Vorfahr genau einmal erstellt wird, selbst wenn mehrere Ketten zu ihm in der Hierarchie führen, und gewährleistet die korrekte Reihenfolge der Initialisierung/Zerstörung.
Beispielcode:
class A { public: int value; A() : value(1) {} }; class B : virtual public A {}; class C : virtual public A {}; class D : public B, public C {}; int main() { D d; d.value = 10; // OK, nur ein A return 0; }
Schlüsselfeatures:
Wenn eine Basisklasse zweimal (links und rechts) geerbt wird, wie viele Exemplare dieser Klasse werden im Objekt im Speicher sein?
Standardmäßig zwei, wenn keine virtuelle Vererbung verwendet wird. Nur bei virtueller Vererbung gibt es genau ein Exemplar.
Kann man explizit angeben, auf welche Basisklasse sich der Zugriff auf ein Mitglied bezieht, wenn Mehrdeutigkeit vorliegt?
Ja, mit Qualifizierern:
d.B::value = 5; d.C::value = 6;
Wie wird die Reihenfolge der Aufrufe von Konstruktoren und Destruktoren im Falle der Mehrfachvererbung bestimmt?
Die Reihenfolge der Aufrufe von Konstruktoren entspricht der Reihenfolge, in der die Basisklassen in der Vererbungsliste deklariert sind (von links nach rechts), gefolgt von der abgeleiteten Klasse. Für Destruktoren ist es umgekehrt.
Ein Programmierer implementiert ein Logging- und Warteschlangensystem über Mehrfachvererbung, ohne über das Diamond Problem Bescheid zu wissen. Infolgedessen wird der gemeinsame Logger zweimal initialisiert, was zu Konflikten bei der Freigabe von Ressourcen führt.
Vorteile:
Nachteile:
Virtuelle Vererbung wird für den gemeinsamen Logger verwendet, und die Mitglieder der Klasse werden explizit im Konstruktor initialisiert.
Vorteile:
Nachteile: