Los destructores virtuales son útiles cuando hagamos un delete de un objeto derivado a través de un puntero de la clase base.
Vayamos al código:
class A {
public:
A() {
cout << "A::ctor" << endl;
}
~A() {
cout << "A::dtor" << endl;
}
};
class B: public A {
public:
B() {
cout << "B::ctor" << endl;
}
~B() {
cout << "B::dtor" << endl;
}
};
...
B *b = new B;
A *a = b;
delete b;
El resultado de este fragmento de código es:
A::ctor
B::ctor
A::dtor
Como vemos no se está destruyendo B, que es la clase hija.
Ahora si declaramos como virtual el destructor de A
...
virtual ~A() {
cout << "A::dtor" << endl;
}
...
B *b = new B;
A *a = b;
delete b;
Da como resultado:
A::ctor
B::ctor
B::dtor
A::dtor
Ahora sí se está destruyendo B.
Conclusiones
Al no ejecutarse el destructor de un objeto, se pueden crear fugas de recursos ya que el constructor de B podría reservar memoria dinámica al construir B y nunca ser liberadas al borrar el objeto de la clase B con un puntero de la clase base, (en este caso la clase A).
Para resumir, siempre declarar como virtual todos los destructores de las clases bases que se van a tratar de manera polifórmica.