Невиртуальный деструктор и clang
Всем привет. Есть вот такой абстрактный класс, в котором я осознанно сделал деструктор не виртуальным:
#include <iostream>
#ifdef _MSC_VER
#define __PRETTY_FUNCTION__ __FUNCSIG__
#endif
class Base
{
public:
virtual void foo() = 0;
~Base()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
Есть потомок:
class Derived : public Base
{
public:
virtual void foo() override
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
~Derived()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
};
И вызов деструктора:
Base * derived = new Derived;
delete derived;
Компилятор и запуск:
$clang --version
Apple clang version 12.0.0 (clang-1200.0.32.28)
Target: x86_64-apple-darwin19.6.0
Thread model: posix
$clang -lstdc++ -std=c++11 example.cpp -o example.out
Ожидаемое поведение этого кода — вызов деструктора только базового класса, так как вызов, очевидно, не виртуальный. В моем случае я получаю исключение на строке определения ~Base():
EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
Опытным путем было выяснено, что проблема возникает если класс абстрактный. Если сделать метод foo виртуальным (virtual void foo() {};) то результат кода предсказуем:
Base::~Base()
Это баг компилятора или ожидаемое поведение? Если второе, то в чем смысл?
Ответы (2 шт):
Такой вызов деструктора вызывает неопределенное поведение. Не гарантируется никакое "будет вызван только деструктор родителя" и т. п.
в чем смысл?
Ну, видимо Clang так помогает вам обнаружить неопределенное поведение - и правильно делает.
Еще пример в тему:
struct A {int x = 0;};
struct B {int y = 0;};
struct C : A, B {};
int main()
{
B *b = new C;
delete b;
}
Вылетает с free(): invalid pointer. Домашнее задание - подумать, почему так происходит.
- Если деструктор не виртуальный, вы даете команду: "уничтожить объект
базового класса(базовую часть объекта) по адресу
derived". А как уничтожать объект, который не возможно создавать, так как класс абстрактный?..(грубая ошибка). А если для наследника нужна память больше? Тогда и при условии, что базовый класс не абстрактный, это ошибка, связанная с не полным освобождением захваченной памяти. - Если деструктор виртуальный, "уничтожаем объект по адресу derived, каков бы он не был". Это и есть правильный подход(иметь виртуальный деструктор в абстрактном классе) .