Проблема удаления объекта через указатель базового класса

Я сразу перейду к примеру:

class B {
    int k{};
   //... НЕТ виртального деструктора
};

class D : public B {
    int n{1};
    //...
};
void foo(B* pb)
{
    //...
    pb = new D;
    //...
    delete pb;
}

Может ли возникать проблема в функции? Если может, то почему или что говорится в стандарте по этому поводу(имеется ввиду для простых типов)?..


Ответы (1 шт):

Автор решения: HolyBlackCat

Стандарт не делает разницы между простыми и сложными типами. Удаление наследника через указатель на родителя без виртуального деструктора - всегда неопределенное поведение.

[expr.delete]/3

In 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;
}
→ Ссылка