Virtuelle Destruktoren

Wenn eine Klasse virtuelle Methoden enthält, sollte sie zusätzlich auch den Destruktor virtuell beschreiben.

Nehmen wir folgendes Szenario an:

#include <stdio.h>
#include <stdlib.h>
 
class Node
{
public:
  Node * Next;
  Node * Prev;
 
  char * UserData; 
 
  Node()
  : Next( NULL )
  , Prev( NULL )   
  {
    UserData = new char[500];
  }  
 
  ~Node()
  {
    printf( "Destruiere Node - loesche UserData\n" );
 
    delete [] UserData; 
  }
};
 
class MyNode : public Node
{
public:
  char * MoreData;
 
  MyNode()
  {
    MoreData = new char[500];
  }  
 
  ~MyNode()
  {
    printf( "Destruiere MyNode - loesche MoreData\n" );
 
    delete [] MoreData; 
  }
};
 
void deleteNode( Node * toDelete )
{
  delete toDelete;
}
 
int main( void )
{
  MyNode *myNode1, *myNode2;
 
  myNode1 = new MyNode;  
  myNode2 = new MyNode;  
 
  deleteNode( myNode1 );
  delete( myNode2 );  
 
  return EXIT_SUCCESS; 
}

Dieses Programm ist nicht objektorientiert, die Funktion deleteNode erhält einen Pointer auf Node und gibt diesen frei. Da der Destruktor nicht virtuell ist, also nicht Objekttyp-Abhängig ist, ruft C++ den Destruktor entsprechend des Datentypes von toDelete - und das ist Node, nicht MyNode.

Schauen wir uns die Ausgaben an:

Destruiere Node - loesche UserData
Destruiere MyNode - loesche MoreData
Destruiere Node - loesche UserData

Obwohl die Funktion deleteNode einen Zeiger auf ein Objekt der Klasse MyNode erhält, geht Information, dass es sich um ein MyNode handelt verloren und C++ sieht in deleteNode nur noch den Zeiger auf ein Node und ruft den Konstruktor, der für Node gilt. Da der Destruktor nicht virtual ist, wird Node::~Node() gerufen. Dies ist aber ja nicht gewünscht, zuerst muss ja der Destruktor MyNode::~MyNode gerufen werden, damit auch der Member MoreData freigegeben wird und dieser Destruktor anschließend den Destruktor seiner Basisklasse ruft.

Um das einzurichten, erklären wir den Destruktor von Node für virtual:

  virtual ~Node()
  {
    printf( "Destruiere Node - loesche UserData\n" );
 
    delete [] UserData; 
  }

Nun weiß delete, dass es den Destruktor in der virtual Table suchen muss und findet dort den Destruktor von ~MyNode. Somit erhalten wir das richtige Ergebnis:

Destruiere MyNode - loesche MoreData
Destruiere Node - loesche UserData
Destruiere MyNode - loesche MoreData
Destruiere Node - loesche UserData