C++ виртуальная функция из конструктора [дубликат]


этот вопрос уже есть ответ здесь:

  • Вызов виртуальных функций внутри конструкторов 12 ответов

почему следующий пример печатает " 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;
}
7 56

7 ответов:

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

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

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

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

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

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

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

вопрос о том, как он работает это пункт FAQ.

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

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

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

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

общее правило заключается в том, что вы не вызываете виртуальную функцию из конструктора.

В C++ нельзя вызвать виртуальный / переопределенный метод из конструктора.

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

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

#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" только для структуры полей и" класс " для структур с полями, методами, конструкторами ...