In C++ gibt es im eigentlichen Sinne kein Konzept eines 'virtuellen Konstruktors', jedoch tritt oft die Notwendigkeit auf, Instanzen von Objekten abgeleiteter Klassen zu erstellen, deren Typ nur zur Laufzeit bekannt ist. Historisch wird ein solcher Fall durch das Muster "virtueller Konstruktor" über virtuelle Funktionen gelöst – normalerweise "clone()" oder "create()".
Historie: In C++ gab es seit den frühen Versionen die Einschränkung, dass Konstruktoren nicht als virtuell deklariert werden können. Dennoch besteht manchmal in Klassenhierarchien die Notwendigkeit, neue Objekte basierend auf bestehenden zu erstellen (oder das vollständige Wissen über den Typ nur zur Laufzeit zu haben).
Problem: Klassischerweise unterliegen Konstruktoren keinem Mechanismus virtueller Funktionen – Aufrufe werden immer zur Compile-Zeit aufgelöst. Dies verhindert eine "lebendige" Fabrik zur Erzeugung von Objekten mit ihrem tatsächlichen zur Laufzeit bekannten Typ über den Konstruktor der Basisklasse.
Lösung: Es wird empfohlen, eine virtuelle Funktion in der Basisklasse zu implementieren – in der Regel ist dies clone() (eine Kopie des Objekts erstellen) oder create() (ein Objekt desselben Typs ohne Zustandskopie erstellen).
Beispielcode:
class Base { public: virtual ~Base() {} virtual Base* clone() const = 0; }; class Derived : public Base { public: Derived(int v) : value(v) {} Base* clone() const override { return new Derived(*this); } private: int value; }; void process(const Base& b) { Base* b2 = b.clone(); // Erstellen einer richtigen Kopie über die virtuelle Methode delete b2; }
Wesentliche Merkmale:
Können Konstruktoren in C++ als virtual deklariert werden?
Nein, die Syntax von C++ lässt den Spezifizierer virtual für Konstruktoren nicht zu. Andernfalls gibt der Compiler einen Kompilierungsfehler aus.
Ist es notwendig, clone() in der Basisklasse als rein virtuell zu definieren, wenn man es selbst deklariert?
Nein, nicht unbedingt. Man kann clone() eine Standardimplementierung geben, zum Beispiel, wenn es sinnvoll ist, nur einen Teil des Zustands zu kopieren oder nullptr zurückzugeben, aber gewöhnlich wird es als rein virtuelle Funktion (pure virtual) gemacht, um mehr Kontrolle zu haben.
Kann man statische Fabrikmethoden als Ersatz für clone() verwenden? Wie verhält sich das zur Virtualität?
Statische Fabrikmethoden sind nicht virtuell und werden in Nachkommen nicht nach dem klassischen Mechanismus überschrieben. Für einen echten "virtuellen Konstruktor" ist eine virtuelle Instanzfunktion erforderlich oder eine andere Methode zur dynamischen Typauflösung.
Ein Entwickler hat das Muster über eine statische Methode implementiert:
class Base { public: static Base* create() { return new Base; } }; class Derived : public Base {}; // ... Base::create() wird aufgerufen, gibt Base zurück, Informationsverlust über den tatsächlichen Typ
Vorteile:
Nachteile:
Code verwendet clone():
class Base { public: virtual ~Base() {} virtual Base* clone() const = 0; }; class Derived : public Base { int x; public: Derived(int x) : x(x) {} Base* clone() const override { return new Derived(*this); } };
Vorteile:
Nachteile: