Wirtualny destruktor to destruktor zadeklarowany z użyciem słowa kluczowego virtual w klasie bazowej. Gwarantuje to wywołanie destruktora klasy pochodnej podczas usuwania obiektu za pomocą wskaźnika na klasę bazową.
class Base { public: virtual ~Base() { /* ... */ } }; class Derived : public Base { public: ~Derived() override { /* ... */ } }; Base* ptr = new Derived(); delete ptr; // Wywołane zostaną oba destruktory
Bez wirtualnego destruktora zachowanie może być niepoprawne — destruktor klasy pochodnej nie zostanie wywołany, co doprowadzi do wycieku zasobów!
Czy należy deklarować destruktor jako wirtualny, jeśli w Twojej klasie bazowej nie ma żadnej funkcji wirtualnej?
Odpowiedź: Tak, jeśli zakłada się dziedziczenie z usuwaniem za pomocą wskaźnika na typ bazowy. Nawet jeśli nie ma innych funkcji wirtualnych, wirtualność destruktora jest niezbędna dla prawidłowego usunięcia potomków.
class Shape { public: virtual ~Shape() {} };
Historia 1
W dużym projekcie biblioteki graficznej podczas usuwania obiektów-rysowników za pomocą wskaźnika na klasę bazową wyciekły zasoby OpenGL (bufory, tekstury) — odpowiednie zwolnienia znajdowały się w destruktorach klas pochodnych, a one nie zostały wywołane.
Historia 2
W bibliotece sieciowej dla protokołu TCP podczas usuwania sesji nie został wywołany destruktor dziedziczących obiektów, zarządzanych przez wspólny interfejs bazowy. Doprowadziło to do masowych zacięć gniazd i wyczerpania limitów deskryptorów.
Historia 3
W systemie embedded na C++ błędnie zadeklarowany (nie wirtualny) destruktor klasy bazowej spowodował wyciek pamięci dla zasobów systemowych kontrolerów wejścia-wyjścia, urządzenia przestawały działać po kilku cyklach aktualizacji sterowników.