Каково ожидаемое поведение инициализации с использованием полиморфизма в C ++? - PullRequest
1 голос
/ 09 апреля 2011

Это должен быть простой 100-уровневый вопрос, но я вижу что-то, чего я не ожидаю в своем проекте, и поиск в сети провалил меня, поэтому я решил спросить здесь Предположим, у меня есть следующий код C ++:

class BaseClass {
public:
  BaseClass() { 
    this->Initialize(); 
  }

  int foo() {
    return this->foo_;
  }

protected:
  virtual int GetInitialValue() { 
    return 1; 
  }

private:
  void Initialize() {
    this->foo_ = this->GetInitialValue();
  }

  int foo_;
};

class DerivedClass : public BaseClass {
public:
  DerivedClass() : BaseClass() {
  }

protected:
  virtual int GetInitialValue() { 
    return 2; 
  }
};

Каким должно быть возвращаемое значение DerivedClass::foo()? Будет ли когда-либо вызываться BaseClass::GetInitialValue(), или всегда будет вызываться DerivedClass::GetInitialValue()? Где я должен был искать, и какие условия поиска я должен использовать, чтобы найти ответ?

Ответы [ 5 ]

4 голосов
/ 09 апреля 2011

GetInitialValue() не является виртуальным, поэтому динамическая отправка в любом случае отсутствует.

В вашей ситуации ожидаемое значение foo() будет равно 1, так как значение _foo установлено в конструкторе BaseClass. Версия производного класса не вызывается, так как сначала вызывается конструктор BaseClass, а члены DerivedClass еще не инициализированы.

Это также элемент № 9 в Действующий C ++ , и этот фрагмент находится на этой странице от artima:

http://www.artima.com/cppsource/nevercall.html

Краткое содержание этой статьи:

Что нужно запомнить

Не вызывайте виртуальные функции во время конструирования или уничтожения, потому что такие вызовы никогда не перейдут в более производный класс, чем класс выполняемого в данный момент конструктора или деструктора

#include <iostream>

class Base {
   public:
   Base() { this->initialize(); }
   void initialize() { this->_foo = this->getInitial(); }
   virtual int getInitial() { return 1; }

   int _foo;
};

class Derived : public Base {
   public:
      Derived() : Base() {}
      virtual int getInitial() { return 2;}
};


int main()
{
   Base* dp = new Derived();
   std::cout << dp->_foo << std::endl;
}

Этот код выводит 1.

2 голосов
/ 09 апреля 2011

Во время выполнения конструкторов и деструкторов динамический тип является составным / разрушенным типом, поэтому в этом случае отправка не будет производной версией. В чем-то вроде

struct Base {
   virtual void f() { std::cout << "Base::f()\n"; }
};

struct D1: Base {
   D1() { f(); }
   virtual void f() { std::cout << "D1::f()\n"; }
};

struct D2: D1 {
   D2() { name = "D2";
   virtual void f() { std::cout << name << "::f()\n"; }
   std::string name;
};

При построении объекта типа D2 отображается "D1 :: f ()", потому что вызов f () происходит во время построения базы D1.

Чтобы понять причину, подумайте, что произойдет, если будет вызван D2 :: f (), элемент name еще не создан ...

2 голосов
/ 09 апреля 2011

Я только что заметил проблему с вашим кодом.Вы косвенно вызываете функцию virtual из конструктора, что очень плохая идея.

Прочитайте эту статью Скотта Мейерса:

Никогда не вызывайте виртуальные функции во время строительства или разрушения

Также прочитайте этот FAQ:

Когда конструктор моего базового класса вызывает виртуальную функцию для своего объекта this, почему не вызывается переопределение этой виртуальной функции производным классом?

0 голосов
/ 09 апреля 2011

Я знаю, что должен ответить на вопрос сам, но я не могу сделать лучше, чем это:

Никогда не вызывайте виртуальные функции во время строительства или разрушения. http://www.artima.com/cppsource/nevercall.html

Цитата из этой статьи:

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

0 голосов
/ 09 апреля 2011

Процесс выглядит следующим образом:

1) Конструктор или Derives называется

2) Он вызывает конструктор базового класса

3) Наборы конструктора базового классаist vtable

4) Выполнено тело конструктора базового класса (в этом конструкторе используется базовый класс vtable)

5) Конструктор производных устанавливает его vtable

6)При этом вызывается конструктор (в этом конструкторе используются производные vtable i)

Google: конструктор виртуального метода c ++

Например: http://www.artima.com/cppsource/nevercall.html

В противном случае,Вы можете искать в черновике стандарта C ++ (http://www.open -std.org / jtc1 / sc22 / wg21 / docs / paper / 2011 / # mailing2011-02) 12.7 4

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...