Virtuelle Konstruktoren

Da ein Konstruktor nie direkt aufgerufen wird, sondern nur beim Erzeugen eines neuen Objekts ein entsprechender Aufruf vom Compiler generiert wird, muß und kann ein Konstruktor nie virtuell sein. Denn aus dem in der Variablendefinition beziehungsweise bei new verwendeten Klassennamen geht eindeutig der Typ des zu erzeugenden Objekts hervor, und damit für den Compiler auch der zu verwendende Konstruktor. Der Compiler kennt die aufzurufende Funktion und verwendet statische Bindung; eine dynamische Bindung ist nicht nötig und auch nicht zulässig.

Trotzdem hat dieser Abschnitt seinen Sinn, denn es gibt Fälle wo eine Funktionen einen Zeiger oder eine Referenz auf ein Objekt obj von einem Typ T hat, aber nicht wissen kann, ob das Objekt wirklich ein T ist oder etwas davon abgeleitetes. Wenn die Funktion nun eine Kopie von obj benötigt, kann sie nicht einfach new T(obj) aufrufen; denn dann würde nur Speicher für ein T allokiert und der copy-Konstruktor der Basisklasse T aufgerufen werden. Wenn aber obj einen von T abgeleiteten Typ hat, müßte gegebenenfalls mehr allokiert werden, und der Konstruktor der abgeleiteten Klasse verwendet werden.

Dieses Problem tritt regelmäßig auf, wenn eine Klasse (beispielsweise C) entworfen wird, die als Container für irgendwelche Elemente vom Typ T (oder davon abgeleitete) dienen soll. In C wird dann ein Zeiger auf die Nutzdaten vom Typ T angelegt werden, der aber auch auf ein von T abgeleitetes Objekt zeigen darf. Zum Einfügen in den Container könnte man eine Methode insert( T &init ) definieren, die von dem übergebenen Objekt init eine Kopie anlegen soll. Wie kann jetzt die Methode C::insert( T &init ) ein neues Objekt beschaffen, das auch dann eine vollwertige Kopie von init ist, wenn init ein von T abgeleitetes Objekt ist? In C::insert( T &init ) kann ein direkter Aufruf von new dies nicht leisten.

Für einen solchen Fall ist in der Klasse T eine virtuelle Methode vorzusehen, die selbst das new ausführt, und einen Zeiger beziehungsweise eine Referenz auf das erzeugte Objekt liefert. Diese Methode könnte als
virtual T&clone( T &init );
deklariert sein, und muß in den von T abgeleiteten Klassen individuell überschrieben werden.

Dann wird in C::insert( T &init ) kein new mehr direkt verwendet, sondern stattdessen clone() aufgerufen:

class T
{
public:

  virtual T *clone() const
  {
    return new T( *this );
  }

  // ...

}; // class T

class TT
  : public T
{
public:

  virtual TT *clone() const
  {
    return new TT( *this );
  }

  // ...

};

class C
{
public:

  C()
  {
    pT = NULL;
  }

  void insert( const T &k )
  {
    delete pT;
    pT = k.clone();
  }

private:

  T  *pT;

  // ...

};

// ...
  T   einT;
  TT  einTT;

  C   c;
  c.insert( einT );  // führt zu T::clone() und damit zu new T
  c.insert( einTT ); // führt zu TT::clone() und damit zu new TT

Auch wenn die clone()-Methode in Wirklichkeit gar kein Konstruktor ist, sondern einen solchen nur indirekt über new aufruft, wird eine solche Funktion oft virtueller Konstruktor genannt. Ein anderer dafür manchmal verwendeter Name ist class factory, was aber m.E. Unfug ist, weil keine Klassen, sondern Objekte (einer Klasse) erzeugt werden.

Zu diesem Thema ist in Ableitungen mit virtuellen Konstruktoren eine vollständige Übungsaufgabe zu finden.

AnyWare@Wachtler.de