W języku C++ nie ma pojęcia 'wirtualnego konstruktora' w dosłownym znaczeniu, jednak często pojawia się potrzeba tworzenia instancji obiektów klas pochodnych na podstawie typu znanego tylko w czasie wykonania. Historycznie taki problem rozwiązuje się za pomocą wzorca „wirtualny konstruktor” przez wirtualne funkcje — zazwyczaj „clone()” lub „create()”.
Historia problemu: W C++ od wczesnych wersji napotkano ograniczenie: konstruktor nie może być zadeklarowany jako wirtualny. Niemniej jednak, czasami w hierarchiach klas konieczne jest tworzenie nowych obiektów na podstawie istniejących (lub pełne poznanie typu dopiero w czasie wykonania).
Problem: Klasycznie, konstruktory nie podlegają żadnemu mechanizmowi wirtualnych funkcji — wywołanie zawsze jest rozwiązywane w czasie kompilacji. To uniemożliwia uzyskanie „żywej” fabryki dla obiektów tworzonych z ich rzeczywistym typem w czasie wykonania przez konstruktor klasy bazowej.
Rozwiązanie: Zaleca się zaimplementowanie wirtualnej funkcji w klasie bazowej — zazwyczaj jest to clone() (utwórz kopię obiektu) lub create() (utwórz obiekt tego samego typu bez kopiowania stanu).
Przykład kodu:
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(); // Tworzymy prawidłową kopię przez wirtualną metodę delete b2; }
Kluczowe cechy:
Czy konstruktory mogą być zadeklarowane jako virtual w C++?
Nie, składnia C++ nie dopuszcza specyfikatora virtual dla konstruktora. W przeciwnym razie kompilator zgłosi błąd kompilacji.
Jeśli zadeklaruję clone() samodzielnie, czy muszę go robić pure virtual w klasie bazowej?
Nie, nie jest to konieczne. Można dać clone() implementację domyślną, na przykład, jeśli ma sens kopiowanie tylko części stanu lub zwracanie nullptr, ale zazwyczaj robi się to jako funkcję czysto wirtualną (pure virtual) dla większej kontroli.
Czy można używać fabrycznych metod statycznych jako zamiennika clone()? Jak to się odnosi do wirtualności?
Metody statyczne fabryk nie są wirtualne i nie są nadpisywane w dziedziczących klasach według klasycznego mechanizmu. Dla prawdziwego „wirtualnego konstruktora” wymagana jest wirtualna funkcja instancji lub inny sposób dynamicznego rozwiązywania typu.
Programista zaimplementował wzorzec przez metodę statyczną:
class Base { public: static Base* create() { return new Base; } }; class Derived : public Base {}; // ... wywoływana jest Base::create(), zwraca Base, utrata informacji o rzeczywistym typie
Zalety:
Wady:
Kod używa 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); } };
Zalety:
Wady: