Wann virtuelle Destruktoren in C++ nötig sind: Analyse 2025

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Virtuelle Destruktoren in C++: Warum das immer noch relevant ist (und wann man es vergessen kann)

\n\n

Wenn Sie jemals in C++ programmiert und mit Vererbung zu tun hatten, haben Sie diese Frage auf Stack Overflow mit Zehntausenden von Aufrufen sicher gesehen: „When to use virtual destructors?“. Im Jahr 2025 hat das Thema nichts an Brisanz verloren – Speicherlecks aufgrund eines fehlenden virtual fordern weiterhin ihren Tribut in der Produktion. Aber es gibt auch eine Kehrseite: Das gedankenlose Hinzufügen von virtual überall, wo es möglich ist, ist ebenfalls keine gute Praxis.

\n\n

Lassen Sie uns ein für alle Mal klären: Wann ein virtueller Destruktor obligatorisch ist, wann er einfach guter Stil ist und wann sein Vorhandensein ein Zeichen für eine schlechte Architektur ist. Los geht's.

\n\n

Kurze Antwort (für Eilige)

\n\n

Ein virtueller Destruktor wird genau dann benötigt, wenn Sie ein Objekt einer abgeleiteten Klasse über einen Zeiger auf die Basisklasse löschen wollen. Wenn es ein solches Szenario im Code nicht gibt, ist virtual nicht erforderlich. Es gibt jedoch Nuancen, die wir unten besprechen werden.

\n\n

Warum ist das überhaupt ein Problem? Anatomie eines Lecks

\n\n

Schauen wir uns ein klassisches Antimuster an, das den Code von Anfängern (und nicht nur von ihnen) zerstört.

\n\n
#include <iostream>\n\nclass Base {\npublic:\n    ~Base() { \n        std::cout << "Base Destruktor\\n"; \n    }\n};\n\nclass Derived : public Base {\nprivate:\n    int* big_data = new int[1000]; // Ressourcen, die freigegeben werden müssen\npublic:\n    Derived() { std::cout << "Derived erstellt\\n"; }\n    ~Derived() {\n        delete[] big_data;\n        std::cout << "Derived Destruktor\\n";\n    }\n};\n\nint main() {\n    Base* ptr = new Derived();\n    delete ptr;  // Hoppla! Es wird nur ~Base() aufgerufen\n    return 0;\n}
\n\n

Ausgabe:

\n
Derived erstellt\nBase Destruktor
\n\n

Das Array big_data ist ausgelaufen. Der Destruktor von Derived wurde nicht aufgerufen, weil der Compiler, der den Typ Base* sieht, den Destruktor genau dieser Klasse aufgerufen hat. Und der ist nicht virtuell.

\n\n

Lösung – Fügen Sie virtual vor dem Destruktor von Base hinzu:

\n\n
class Base {\npublic:\n    virtual ~Base() = default;  // oder leerer Rumpf\n};
\n\n

Jetzt wird bei delete ptr zuerst ~Derived() ausgeführt, dann ~Base(). Die Ressourcen sind freigegeben, der Speicher läuft nicht, der statische Analysator ist zufrieden.

\n\n

Wann ein virtueller Destruktor obligatorisch ist (eiserne Regel)

\n\n

Es gibt nur einen unbedingten Fall: Wenn eine Klasse für die polymorphe Verwendung über einen Zeiger oder eine Referenz auf die Basisklasse vorgesehen ist und Objekte abgeleiteter Klassen über diesen Zeiger gelöscht werden.

\n\n

In der Praxis bedeutet das:

\n
    \n
  • Die Klasse hat mindestens eine virtuelle Funktion (rein oder nicht)
  • \n
  • Sie planen, Base* oder Base& für die Arbeit mit Derived zu verwenden
  • \n
  • Objekte werden auf dem Heap erstellt (new) und über delete gelöscht
  • \n
\n\n

Beispiel – eine klassische Formenhierarchie:

\n\n
class Shape {\npublic:\n    virtual double area() const = 0;\n    virtual ~Shape() = default;  // obligatorisch für abstrakte Klasse\n};\n\nclass Circle : public Shape {\n    double radius;\npublic:\n    explicit Circle(double r) : radius(r) {}\n    double area() const override { return 3.14159 * radius * radius; }\n};\n\nint main() {\n    std::vector<std::unique_ptr<Shape>> shapes;\n    shapes.push_back(std::make_unique<Circle>(5.0));\n    // unique_ptr kümmert sich selbst um delete, aber der Destruktor von Shape muss virtual sein\n    return 0;\n}
\n\n

Beachten Sie: Selbst wenn Sie std::unique_ptr verwenden (und das ist der empfohlene Ansatz im Jahr 2025), wird der virtuelle Destruktor trotzdem benötigt. unique_ptr ruft delete über einen Zeiger auf die Basisklasse auf –

Blogs

Buchempfehlungen