Оба delete через Base* имеют неопределённое поведение: деструктор Base не виртуальный. Наличие другой виртуальной функции не делает деструктор виртуальным автоматически.
Проблемы деструкторов при удалении через указатель базового класса
Оба delete через Base* имеют неопределённое поведение: деструктор Base не виртуальный. Наличие другой виртуальной функции не делает деструктор виртуальным автоматически.
Короткий ответ
Что ответить на собеседовании
Подробный разбор
Ответ с пояснениями
Условие
Опишите, в чём заключается проблема, возникающая при удалении объектов через указатель базового типа (Base*). Какой результат будет при выполнении delete baseChild; и delete baseChildChild;? Объясните, почему такое поведение считается опасным.
struct Base {
virtual void baseFunc0() {
std::cout << "Base::baseFunc0" << std::endl;
}
};
struct Child : Base {
void baseFunc0() override {
std::cout << "Child::baseFunc0" << std::endl;
}
};
struct ChildChild : Child {
void baseFunc0() override {
std::cout << "ChildChild::baseFunc0" << std::endl;
}
};
void run() {
Base base;
Base* baseChild = new Child;
Base* baseChildChild = new ChildChild;
base.baseFunc0();
baseChild->baseFunc0();
baseChildChild->baseFunc0();
delete baseChild;
delete baseChildChild;
}
Ответ
Оба delete через Base* имеют неопределённое поведение: деструктор Base не виртуальный. Наличие другой виртуальной функции не делает деструктор виртуальным автоматически.
Добавьте в Base:
virtual ~Base() = default;
Тогда при удалении ChildChild сначала разрушится производная часть, затем Child и Base. Вызовы baseFunc0 до удаления выбирают соответствующие переопределения, но для всей исходной программы с неопределённым поведением нельзя обещать полный конкретный вывод.
Это не просто риск пропустить очистку полей: правило нарушено даже у показанных пустых по данным производных классов.