Проблема удаления объекта через указатель базового класса
Я сразу перейду к примеру:
class B {
int k{};
//... НЕТ виртального деструктора
};
class D : public B {
int n{1};
//...
};
void foo(B* pb)
{
//...
pb = new D;
//...
delete pb;
}
Может ли возникать проблема в функции? Если может, то почему или что говорится в стандарте по этому поводу(имеется ввиду для простых типов)?..
Ответы (1 шт):
Стандарт не делает разницы между простыми и сложными типами. Удаление наследника через указатель на родителя без виртуального деструктора - всегда неопределенное поведение.
[expr.delete]/3In a single-object delete expression, if the static type of the object to be deleted is not similar ([conv.qual]) to its dynamic type and the selected deallocation function (see below) is not a destroying operator delete, the static type shall be a base class of the dynamic type of the object to be deleted and the static type shall have a virtual destructor or the behavior is undefined.
Вольный перевод: если вызвать delete на указателе типа A *, который на самом деле указывает на объект типа B, то B должен быть наследником A, и у A должен быть виртуальный деструктор, иначе поведение не определено.
На практике да, если B не добавляет новых полей с непустыми деструкторами, то ничего не должно сломаться.
Но можно словить краш, если экземпляр родителя находится не в самом начале потомка, а где-то в середине (потому что перед ним - другой родитель, или указатель на виртуальную таблицу). Из-за этого в функцию, освобождающую память (operator delete()) приходит не тот адрес.
struct A {int x;};
struct C : A {virtual void foo() {}};
int main()
{
A *a = new C;
delete a;
}
struct A {int x;};
struct B {int y;};
struct C : A, B {};
int main()
{
B *b = new C;
delete b;
}
struct A {int x;};
struct C : virtual A {};
int main()
{
A *a = new C;
delete a;
}