Hintergrund der Frage:
In C++ wurde die Unterstützung für objektorientierte Programmierung eingeführt, die grundlegend für moderne Sprachen ist. Zur Umsetzung von Polymorphismus wurden virtuelle Funktionen verwendet. Dadurch war es möglich, die gewünschte Implementierung einer Methode zur Laufzeit und nicht nur zur Kompilierungszeit aufzurufen, was für eine Architektur mit Vererbung entscheidend ist.
Problem:
Ein häufiger Fehler ist die Verwirrung zwischen statischem und dynamischem Methodenaufruf, vergessene virtuelle Destruktoren und der falsche Umgang mit Vererbung (z.B. Object Slicing, Aufruf der Basisversion anstelle von Override). Oft wird verwechselt, wann Polymorphismus tatsächlich funktioniert.
Lösung:
Eine virtuelle Funktion wird mit dem Schlüsselwort virtual in der Basisklasse deklariert und kann im abgeleiteten Klassen überschrieben werden (override). Wenn die Funktion über einen Zeiger oder eine Referenz auf die Basisklasse aufgerufen wird, wird die Version aus der abgeleiteten Klasse ausgeführt.
Beispielcode:
struct Base { virtual void foo() { std::cout << "Base::foo "; } }; struct Derived : Base { void foo() override { std::cout << "Derived::foo "; } }; void call(Base& b) { b.foo(); } int main() { Derived d; call(d); // Gibt Derived::foo aus }
Wichtige Merkmale:
Funktioniert Polymorphismus bei der Übergabe von Objekten durch Wert?
Nein. Die Übergabe durch Wert führt zu "Slicing" — es wird nur der Teil kopiert, der dem Typ des Parameters entspricht (normalerweise die Basisklasse), und Polymorphismus wird deaktiviert.
Beispielcode:
void call(Base b) { b.foo(); } // immer Aufruf von Base::foo
Muss der Destruktor in der Basisklasse als virtuell deklariert werden?
Ja, wenn erwartet wird, dass abgeleitete Objekte über einen Zeiger auf die Basisklasse gelöscht werden. Andernfalls tritt ein Speicherleck oder Ressourcenfreigabefehler auf.
Beispielcode:
struct Base { virtual ~Base() {} };
Was passiert, wenn das Schlüsselwort override im abgeleiteten Klassen nicht verwendet wird?
Wenn im abgeleiteten Klass das override nicht angegeben wird, aber versehentlich die Signatur der Funktion geändert wird (z.B. const weggelassen oder in den Parametern falsch), wird die virtuelle Funktion nicht überschrieben, es wird eine neue erstellt, und Polymorphismus funktioniert nicht wie erwartet.
Der Programmierer hat den Destruktor der Basisklasse nicht als virtuell deklariert; das Löschen eines Arrays von Objekten über einen Basiszeiger führte zu einem Speicherleck.
Vorteile:
Nachteile:
Ein virtueller Destruktor wurde deklariert; es wurden nur Referenzen/Zeiger auf den Basistyp verwendet. Polymorphismus funktionierte korrekt.
Vorteile:
Nachteile: