суббота, 23 марта 2013 г.

C++: конструкторы и виртуальные функции

Как говорится, век - живи, век учись.

Захотелось мне вынести инициализацию данных класса в виртуальную функцию init(), с тем, чтобы классы-наследники могли переопределить ее и добавить в инициализацию что-то своё. Примерно так:

  1. class C1
  2. {
  3. protected:
  4.     int m_count;
  5.     virtual void init() { m_count = 1; }
  6. public:
  7.     C1() { init(); }
  8.     inline int count() const { return m_count; }
  9. };
  10.  
  11. class C2: public C1
  12. {
  13. protected:
  14.     virtual void init() { C1::init(); m_count = m_count+1; }
  15. public:
  16.     C2() : C1() {}
  17. };
  18.  
  19. int _tmain(int argc, _TCHAR* argv[])
  20. {
  21.     C1 c1;
  22.     _tprintf(_T("C1 count: %i\n"), c1.count());
  23.     C2 c2;
  24.     _tprintf(_T("C2 count: %i\n"), c2.count());
  25.     return 0;
  26. }

Результат получается такой:

C1 count: 1
C2 count: 1

Опа! Оказывается переопределенная в классе C2 функция init() не вызывается. Получается, что если вызвать из конструктора класса виртуальную функцию этого же класса, то обычный метод вызова виртуальных функций через vtable задействован не будет - функция будет вызвана как если бы они была не-виртуальной. По-моему такое поведение не выглядит логичным, поэтому от вызова виртуальных функций из конструктора лучше отказаться.

PS. Из деструктора вызывать виртуальные функции тоже не следует.

По поводу того, что при вызове виртуальных функций из конструктора (и деструктора) не используется vtable, - тут я, похоже, был не прав. vtable используется, только в конструкторе класса C1 (см. пример) используется vtable класса C1, а не класса-потомка C2 - вот в чем дело. Аналогично, в деструкторе C1 также используется vtable класса С1.

===
Перепечатка материалов блога разрешается с обязательной ссылкой на blog.coolsoftware.ru

Комментариев нет:

Отправить комментарий