Виртуальные функции в конструкторе и деструкторе

Рассмотрим простой пример (virtual_funct_const.cpp):

#include <iostream>

class A {
 public:
  A() {
    construct();
  }

  ~A() {
    destruct();
  }

  virtual void construct() {
    std::cout << "A::construct()" << std::endl;
  }

  virtual void destruct() {
    std::cout << "A::destruct()" << std::endl;
  }
};

class B: public A {
 public:
  B() {
    construct();
  }

  ~B() {
    destruct();
  }

  virtual void construct() {
    std::cout << "B::construct()" << std::endl;
  }

  virtual void destruct() {
    std::cout << "B::destruct()" << std::endl;
  }
};

int main() {
  B b;
  return 0;
}

Что напечатает эта программа?

А вот что:

A::construct()
B::construct()
B::destruct()
A::destruct()

Получается, что конструкторы и деструкторы классов A и B при вызове объявленных виртуальными функций construct() и destruct() реально вызывали функции только своего класса.

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

Правило надо заучивать, что неудобно. Проще понять принцип. А принцип тут в краеугольном камне реализации наследования в C++: при создании объекта конструкторы в иерархии вызываются от базового класса к самому последнему унаследованному. Для деструкторов все наоборот.

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

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

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


Оригинальный пост | Disclaimer

Комментарии