Не вызывайте виртуальные функции в конструкторах и деструкторах
Причина
Вызываемая функция будет той, которая относится к объекту, построенному до сих пор, а не к возможно переопределяющей функции в производном классе. Это может быть очень запутанным. Хуже того, прямой или косвенный вызов нереализованной чистой виртуальной функции из конструктора или деструктора приводит к неопределенному поведению.
Плохой пример
class Base {
public:
virtual void f() = 0; // не реализовано
virtual void g(); // реализовано с версией Base
virtual void h(); // реализовано с версией Base
virtual ~Base(); // реализовано с версией Base
};
class Derived : public Base {
public:
void g() override; // предоставить реализацию Derived
void h() final; // предоставить реализацию Derived
Derived()
{
// ПЛОХО: попытка вызвать нереализованную виртуальную функцию
f();
// ПЛОХО: вызовет Derived::g, а не виртуальную диспетчеризацию дальше
g();
// ХОРОШО: явно указать намерение вызвать только видимую версию
Derived::g();
// хорошо, квалификация не нужна, h является final
h();
}
};
Обратите внимание, что вызов конкретной явно квалифицированной функции не является виртуальным вызовом, даже если функция virtual.
См. также фабричные функции для выполнения эффекта вызова функции производного класса без риска неопределенного поведения.
Примечание
Внутри себе нет ничего плохого в вызове виртуальных функций из конструкторов и деструкторов. Семантика таких вызовов является типобезопасной. Однако опыт показывает, что такие вызовы редко необходимы, легко запутывают разработчиков и становятся источником ошибок при использовании новичками.
Применение
- Отметьте вызовы виртуальных функций из конструкторов и деструкторов.