Función virtual de C ++ desde constructor

¿Por qué el siguiente ejemplo imprime “0” y qué debe cambiar para que imprima “1” como esperaba?

#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; } 

Porque la base se construye primero y no ha “madurado” en un derived todavía. No puede invocar métodos en un objeto cuando no puede garantizar que el objeto ya esté inicializado correctamente.

Cuando se construye un objeto derivado, antes de que se llame al cuerpo del constructor de la clase derivada, se debe completar el constructor de la clase base. Antes de que se llame al constructor de clase derivado, el tipo dynamic del objeto en construcción es una instancia de clase base y no una instancia de clase derivada. Por este motivo, cuando llama a una función virtual desde un constructor, solo se pueden invocar las anulaciones de funciones virtuales de la clase base.

En realidad, hay una manera de obtener este comportamiento. “Todos los problemas del software se pueden resolver con un nivel de indirección”.

 /* 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( ) ) { } }; 

No debe llamar polimórficamente los métodos virtuales desde el constructor. En su lugar, puede llamarlos después de la construcción del objeto.

Su código puede ser re escrito de la siguiente manera

 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 cuestión de cómo funciona es un elemento de Preguntas frecuentes .

Resumiendo, mientras se construye la clase T , el tipo dynamic es T , que impide las llamadas virtuales a las implementaciones de funciones de clase derivadas, que si se permite podrían ejecutar código antes de que se haya establecido el invariante de clase relevante (un problema común en Java y C #, pero C ++ es seguro a este respecto).

La cuestión de cómo hacer una inicialización derivada específica de clase en un constructor de clase base también es un elemento de preguntas frecuentes , directamente después del mencionado anteriormente.

Resumiendo, el uso de polymorphism estático o dynamic puede pasar las implementaciones de funciones relevantes hasta el constructor (o clase) de la clase base.

Una forma particular de hacerlo es pasar un objeto de “fábrica de piezas” , donde este argumento puede ser predeterminado. Por ejemplo, una clase de Button general puede pasar una función API de creación de botones hasta su constructor de clase base de Widget , para que ese constructor pueda crear el objeto de nivel API correcto.

La regla general es que no llamas a una función virtual desde un constructor.

En C ++, no puede llamar a un método virtual / modificado desde un constructor.

Ahora, hay una buena razón por la que puedes hacer esto. Como una “mejor práctica en software”, debe evitar llamar métodos adicionales desde su constructor, incluso no virtuales, como sea posible.

Sin embargo, siempre hay una excepción a la regla, por lo que puede utilizar un “método de pseudo constructor” para emularlas:

 #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(); } 

Como una ventaja, recomiendo a los progtwigdores que utilicen “struct” para estructuras de solo campos, y “class” para estructuras con campos, métodos, constructores, …