Aby zapewnić właściwe usuwanie obiektów, należy deklarować destruktor jako funkcję wirtualną, jeśli tylko mamy zamiar korzystać z publicznego dziedziczenia z danej klasy. Wywołując wtedy destruktor (poprzez operator delete) obiektu klasy pochodnej wskazywanego przez wskaźnik do klasy bazowej, wywołamy naprawdę destruktor dla całego obiektu. Dzięki polimorfizmowi wywołany będzie bowiem wtedy destruktor z klasy pochodnej, a destruktor podobiektu klasy bazowej będzie potem wywołany i tak, według normalnych zasad kolejności wywoływania destruktorów. Jak bowiem mówiliśmy, przy usuwaniu obiektu klasy pochodnej najpierw wykonywany jest destruktor dla części „własnej”, a potem automatycznie wywoływany jest destruktor dla podobiektu klasy bazowej. Gdyby destruktor nie był wirtualny, to ponieważ typem statycznym jest wskaźnik do obiektu klasy bazowej, wywołany byłby od razu destruktor z tej klasy, który jednak nie wie na przykład o istnieniu składowych czy zasobów dodanych w klasie pochodnej.
W poniższym przykładzie obiekt klasy pochodnej
Pelne
 jest
wskazywany przez wskaźnik
osoba
 typu
Nazwisko*, a
więc wskaźnik do obiektu klasy bazowej
Nazwisko.
      1.  #include <iostream>
      2.  using namespace std;
      3.  
      4.  class Nazwisko {
      5.      char* nazwis;
      6.  public:
      7.      Nazwisko(const char* n)
      8.          : nazwis(strcpy(new char[strlen(n)+1], n))
      9.      {
     10.          cout << "Ctor Nazwisko: " << nazwis << endl;
     11.      }
     12.  
     13.      virtual
     14.      ~Nazwisko() {
     15.          cout << "Dtor Nazwisko: " << nazwis << endl;
     16.          delete [] nazwis;
     17.      }
     18.  };
     19.  
     20.  class Pelne : public Nazwisko {
     21.      char* imie;
     22.  public:
     23.      Pelne(const char* i, const char* n)
     24.          : Nazwisko(n),
     25.            imie(strcpy(new char[strlen(i)+1], i))
     26.      {
     27.          cout << "Ctor Pelne, Imie: " << imie << endl;
     28.      }
     29.  
     30.      ~Pelne() {
     31.          cout << "Dtor Pelne, Imie: " << imie << endl;
     32.          delete [] imie;
     33.      }
     34.  };
     35.  
     36.  int main() {
     37.      Nazwisko* osoba = new Pelne("Jan", "Malinowski");
     38.      delete osoba;
     39.  }
    Ctor Nazwisko: Malinowski
    Ctor Pelne, Imie: Jan
    Dtor Pelne, Imie: Jan
    Dtor Nazwisko: Malinowski
Gdyby destruktor w klasie bazowej
Nazwisko
 nie był
zadeklarowany jako wirtualny (po wykomentowaniu linii 13), to wywołany
byłby tylko destruktor z klasy określanej przez statyczny typ
wskaźnika, a więc z klasy
Nazwisko:
    Ctor Nazwisko: Malinowski
    Ctor Pelne, Imie: Jan
    Dtor Nazwisko: Malinowski
i, jak widać, imię w ogóle nie zostałoby usunięte!
Zauważmy, że mamy tu do czynienia z pewną niekonsekwencją: destruktor w klasie pochodnej, ˜Full, przesłania destruktor z klasy bazowej, ˜Name, choć ich nazwy są inne. Pod tym względem destruktor jest wyjątkowy: w innych przypadkach metoda przesłaniająca musi oczywiście mieć tę samą nazwę co metoda przesłaniana.
T.R. Werner, 21 lutego 2016; 20:17