Funzione virtuale C ++ dal costruttore

Perché il seguente esempio stampa “0” e cosa deve cambiare per poter stampare “1” come mi aspettavo?

#include  struct base { virtual const int value() const { return 0; } base() { std::cout << value() << std::endl; } virtual ~base() {} }; struct derived : public base { virtual const int value() const { return 1; } }; int main(void) { derived example; } 

Perché la base è costruita prima e non è ancora “maturata” in una derived . Non può chiamare metodi su un object quando non può garantire che l’object sia già inizializzato correttamente.

Quando viene costruito un object derivato, prima che il costruttore del costruttore della class derivata sia chiamato deve essere completato il costruttore della class base. Prima che venga chiamato il costruttore della class derivata, il tipo dinamico dell’object in costruzione è un’istanza di class base e non un’istanza di class derivata. Per questo motivo, quando si chiama una funzione virtuale da un costruttore, è ansible chiamare solo le sostituzioni delle funzioni virtuali della class base.

In realtà, c’è un modo per ottenere questo comportamento. “Ogni problema nel software può essere risolto con un livello di riferimento indiretto.”

 /* Disclaimer: I haven't done C++ in many months now, there might be a few syntax errors here and there. */ class parent { public: parent( ) { /* nothing interesting here. */ }; protected: struct parent_virtual { virtual void do_something( ) { cout < < "in parent."; } }; parent( const parent_virtual& obj ) { obj.do_something( ); } }; class child : public parent { protected: struct child_virtual : public parent_virtual { void do_something( ) { cout << "in child."; } }; public: child( ) : parent( child_virtual( ) ) { } }; 

Non dovresti chiamare polimorficamente i metodi virtuali dal costruttore. Invece puoi chiamarli dopo la costruzione dell’object.

Il tuo codice può essere riscritto come segue

 struct base { virtual const int value() const { return 0; } base() { /* std::cout < < value() << std::endl; */ } virtual ~base() {} }; struct derived : public base { virtual const int value() const { return 1; } }; int main(void) { derived example; std::cout << example.value() << std::endl; } 

La domanda su come funziona è una voce FAQ .

Riassumendo, mentre la class T è in costruzione, il tipo dinamico è T , che impedisce le chiamate virtuali alle implementazioni di funzioni di class derivate, che se consentito possono eseguire codice prima dell’invarianza della class rilevante stabilita (un problema comune in Java e C #, ma C ++ è al sicuro in questo senso).

La domanda su come eseguire l’inizializzazione specifica della class derivata in un costruttore della class base è anche una voce FAQ , che segue direttamente quella citata in precedenza.

Riassumendo, usando il polimorfismo statico o dinamico su può passare le implementazioni di funzioni rilevanti fino al costruttore della class base (o class).

Un modo particolare per farlo è passare un object “fabbrica di parti” su, dove questo argomento può essere predefinito. Ad esempio, una class Button generica potrebbe passare una funzione API di creazione pulsanti fino al relativo costruttore della class base Widget , in modo che il costruttore possa creare l’object livello API corretto.

La regola generale è che non si chiama una funzione virtuale da un costruttore.

In C ++, non è ansible chiamare un metodo virtuale / sovrascritto da un costruttore.

Ora, c’è una buona ragione per farlo. Come “best practice in software”, dovresti evitare di chiamare metodi aggiuntivi dal costruttore, anche non virtuali, il più ansible.

Ma c’è sempre un’eccezione alla regola, quindi potresti voler usare un “metodo pseudo costruttore” per emularle:

 #include  class base { //  base() { // do nothing in purpouse } //  //  ~base() { // do nothing in purpouse } //  //  public virtual void create() { // move code from static constructor to fake constructor std::cout < < value() << std::endl; } //  //  public virtual void destroy() { // move code from static destructor to fake destructor // ... } //  public virtual const int value() const { return 0; } public virtual void DoSomething() { // std:cout < < "Hello World"; } }; class derived : public base { //  public override void create() { // move code from static constructor to fake constructor std::cout < < "Im pretending to be a virtual constructor," << std::endl; std::cout << "and can call virtual methods" << std::endl; } //  //  public override void destroy() { // move code from static destructor to fake destructor std::cout < < "Im pretending to be a virtual destructor," << std::endl; std::cout << "and can call virtual methods" << std::endl; } //  public virtual const int value() const { return 1; } }; int main(void) { // call fake virtual constructor in same line, after real constructor derived* example = new example(); example->create(); // do several stuff with your objects example->doSomething(); // call fake virtual destructor in same line, before real destructor example->destroy(); delete example(); } 

Come plus, raccomando ai programmatori di usare “struct” solo per le strutture dei campi e “class” per le strutture con campi, metodi, costruttori, …