Nel linguaggio C++ non esiste il concetto di 'costruttore virtuale' nel senso diretto, tuttavia la necessità di creare istanze di oggetti di classi derivate il cui tipo è noto solo durante l'esecuzione si presenta spesso. Storicamente, un'analogia per questa sfida è stata risolta tramite il pattern "costruttore virtuale" attraverso funzioni virtuali - tipicamente "clone()" o "create()".
Storia della questione: In C++ fin dalle prime versioni c'è stata la limitazione: i costruttori non possono essere dichiarati virtuali. Tuttavia, a volte nelle gerarchie di classi è necessario creare nuovi oggetti sulla base di uno esistente (o con la conoscenza completa del tipo solo al momento dell'esecuzione).
Problema: Tradizionalmente, i costruttori non seguono alcun meccanismo di funzioni virtuali — la chiamata viene sempre risolta al momento della compilazione. Ciò non consente di ottenere una "fabbrica viva" per gli oggetti generati con il loro vero tipo di tempo di esecuzione tramite il costruttore della classe base.
Soluzione: Si consiglia di implementare una funzione virtuale nella classe base — tipicamente clone() (crea una copia dell'oggetto) o create() (crea un oggetto dello stesso tipo senza copiare lo stato).
Esempio di codice:
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(); // Creiamo una copia corretta tramite metodo virtuale delete b2; }
Caratteristiche chiave:
Possono i costruttori essere dichiarati virtuali in C++?
No, la sintassi C++ non consente il modificatore virtual per i costruttori. In caso contrario, il compilatore genererà un errore di compilazione.
Se dichiaro clone() io stesso, è obbligatorio farlo pure virtual nella classe base?
No, non è obbligatorio. È possibile fornire a clone() un'implementazione predefinita, ad esempio, se ha senso copiare solo parte dello stato o restituire nullptr, ma di solito è fatto come funzione virtuale pura (pure virtual) per un maggiore controllo.
Si possono usare metodi statici di fabbrica come sostituzione di clone()? Come si relaziona alla virtualità?
I metodi statici della fabbrica non sono virtuali e non vengono sovrascritti nei discendenti con il meccanismo classico. Per un vero "costruttore virtuale" è necessaria una funzione virtuale di istanza o un altro modo di risoluzione dinamica del tipo.
Uno sviluppatore ha implementato il pattern tramite un metodo statico:
class Base { public: static Base* create() { return new Base; } }; class Derived : public Base {}; // ... viene chiamato Base::create(), restituisce Base, perdita di informazione sul tipo reale
Vantaggi:
Svantaggi:
Il codice utilizza 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); } };
Vantaggi:
Svantaggi: