Виртуальная функция С++ от конструктора

Почему в следующем примере печатается "0", и что должно измениться для его печати "1", как я ожидал?

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

Ответ 1

Потому что base построен первым и еще не "созрел" в derived. Он не может вызывать методы для объекта, если он не может гарантировать, что объект уже правильно инициализирован.

Ответ 2

Когда производный объект строится, перед тем, как тело конструктора производного класса называется конструктором базового класса, должен завершиться. Прежде чем конструктор производного класса называется динамическим типом строящегося объекта, это экземпляр базового класса, а не экземпляр производного класса. По этой причине при вызове виртуальной функции от конструктора можно вызвать только переопределение виртуальных функций базового класса.

Ответ 3

Собственно, есть способ получить это поведение. "Каждая проблема в программном обеспечении может быть решена с уровнем косвенности".

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

Ответ 4

Вы не должны полиморфно вызывать виртуальные методы из конструктора. Вместо этого вы можете вызвать их после создания объекта.

Ваш код можно записать следующим образом

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

Ответ 5

Вопрос о том, как это работает является элементом часто задаваемых вопросов.

Подводя итог, в то время как класс T строится, динамический тип T, который предотвращает виртуальные вызовы для реализации функций производного класса, которые, если разрешено, могут выполнять код до того, как был установлен соответствующий инвариант класса (общая проблема в Java и С#, но С++ в этом отношении безопасен).

Вопрос о том, как выполнить инициализацию с производными производными в конструкторе базового класса, также элемент часто задаваемых вопросов, непосредственно следуя ранее упомянутому.

Подведение итогов с использованием статического или динамического полиморфизма может передать соответствующие реализации функций до конструктора базового класса (или класса).

Один из способов сделать это - передать "parts factory" объект, где это аргумент может быть отключен. Например, общий класс Button может передать функцию API создания кнопок до своего конструктора базового класса Widget, так что этот конструктор может создать правильный объект уровня API.

Ответ 6

Общее правило: вы не вызываете виртуальную функцию из конструктора.

Ответ 7

В С++ вы не можете вызвать метод virtual/overriden из конструктора.

Теперь есть веская причина, по которой вы можете это сделать. В качестве "лучшей практики в программном обеспечении" вам следует избегать вызова дополнительных методов из вашего конструктора, даже не виртуального, насколько это возможно.

Но всегда есть исключение из правила, поэтому вы можете использовать "метод псевдоконструктора", чтобы имитировать их:

#include <iostream>

class base {
   // <constructor>
   base() {
      // do nothing in purpouse
   }
   // </constructor>

   // <destructor>
   ~base() {
      // do nothing in purpouse
   }
   // </destructor>

   // <fake-constructor>
   public virtual void create() {
      // move code from static constructor to fake constructor
      std::cout << value() << std::endl;
   }
   // </fake-constructor>

   // <fake-destructor>
   public virtual void destroy() {
      // move code from static destructor to fake destructor
      // ...
   }
   // </fake-destructor>

   public virtual const int value() const {
      return 0;
   }

   public virtual void DoSomething() {
      // std:cout << "Hello World";
   }
};

class derived : public base {
   // <fake-constructor>
   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;
   }
   // </fake-constructor>


   // <fake-destructor>
   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;
   }
   // </fake-destructor>

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

В качестве плюса я рекомендую программистам использовать "struct" только для структур полей и "класс" для структур с полями, методами, конструкторами,...