ProgrammierungC++ Entwickler

Wie funktioniert der Mechanismus der virtuellen Funktionen und der virtuellen Vererbung in C++? Welche Besonderheiten gibt es beim Entwerfen von Klassen mit abstraktem Interface?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort

Virtuelle Funktionen ermöglichen es, Polymorphismus zu implementieren – die Fähigkeit, Objekte von abgeleiteten Klassen über Zeiger oder Referenzen auf die Basisklasse zu behandeln. Um eine virtuelle Funktion zu deklarieren, wird das Schlüsselwort virtual verwendet:

class Base { public: virtual void foo() { std::cout << "Base::foo"; } }; class Derived : public Base { public: void foo() override { std::cout << "Derived::foo"; } };

Der Aufruf von foo() über einen Zeiger vom Typ Base* ruft die Implementierung in Derived auf, wenn er auf ein Objekt von Derived zeigt.

Abstrakte Klasse – enthält mindestens eine rein virtuelle Funktion:

class Interface { public: virtual void process() = 0; // rein virtuelle Funktion };

Virtuelle Vererbung löst das Problem der diamantförmigen Vererbung (diamond problem):

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

Das garantiert, dass im Objekt D nur eine einzige Instanz der Basisklasse A vorhanden ist.

Fangfrage

Was ist der Unterschied zwischen einem virtuellen Destruktor und einem normalen? Muss man den Destruktor virtuell machen, wenn die Klasse zur Vererbung gedacht ist?

Antwort: Ein virtueller Destruktor stellt sicher, dass beim Löschen über einen Zeiger des Basistyps der Destruktor der abgeleiteten Klasse aufgerufen wird. Das ist wichtig für die korrekte Freigabe von Ressourcen.

class Base { public: virtual ~Base() {} };

Wenn der Destruktor nicht virtuell ist, wird beim delete BasePtr; nur der Destruktor von Base für das Objekt der abgeleiteten Klasse aufgerufen, und die Ressourcen der geerbten Felder werden nicht freigegeben.

Beispiele für reale Fehler aufgrund fehlenden Wissens über die Feinheiten des Themas


Geschichte

In einem großen Finanzsystem, in dem für verschiedene Instrumente ein gemeinsames Interface der Klasse verwendet wurde, war der virtuelle Destruktor nicht deklariert. In einem der Abkömmlinge wurde eine dynamische Ressource verwendet. Beim Löschen des Objekts über einen Zeiger auf den Basistyp kam es zu einem Speicherleck, das erst in der Produktionsphase entdeckt wurde.


Geschichte

Das Team verwendete multiple Vererbung, ohne die virtuelle Vererbung anzuwenden. Klasse D erbte A zweimal über Zwischenklassen. Das führte zu einer Verdopplung des Zustands und zu Fehlern beim Zugriff auf Mitglieder von A. Dies wurde erst nach einem Audit mit statischen Analysetools behoben.


Geschichte

Im Projekt zur Entwicklung eines Logging-Plugins wurde eine abstrakte Klasse verwendet, aber der Destruktor wurde nicht virtuell gemacht. Beim Löschen von Plugins über einen Zeiger auf das Interface traten nicht offensichtliche Abhängigkeiten und Bugs auf, die mit nicht aufgerufenen Destruktoren von Nachkommen verbunden waren. Das Problem betraf den Ressourcenpool und führte zu Ressourcenverlusten.