Ответ
При вызове виртуальной функции из деструктора (или конструктора) базового класса происходит статическое связывание. Это означает, что будет вызвана версия функции, определенная в текущем классе, а не переопределенная в производном классе.
Причина: Порядок построения/разрушения объектов в C++ строго определен. При вызове деструктора базового класса производная часть объекта уже считается разрушенной. Вызов переопределенного метода из производного класса в этот момент привел бы к неопределенному поведению, так как члены производного класса могли быть уже уничтожены или находиться в невалидном состоянии.
Пример:
#include <iostream>
class Base {
public:
virtual void log() { std::cout << "Base::log()n"; }
virtual ~Base() {
log(); // Всегда вызовет Base::log(), даже для объекта Derived
}
};
class Derived : public Base {
public:
void log() override { std::cout << "Derived::log()n"; }
~Derived() {
// Деструктор Derived уже завершил работу к моменту вызова ~Base()
}
};
int main() {
Derived d; // При разрушении выведет "Base::log()"
return 0;
}
Практическое следствие: Не стоит полагаться на полиморфное поведение внутри деструкторов и конструкторов. Если нужна общая логика очистки, лучше вынести ее в отдельный невиртуальный protected-метод и явно вызывать его из деструкторов производных классов.