0

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

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

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

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

AR Hovsepyan
  • 15,934

1 Answers1

5

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

[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;
}
HolyBlackCat
  • 27,445
  • 3
  • 27
  • 40
  • как это получается что со случаем виртуального наследования указатель на родитедя указывает в середину?? в какую середину? Там же всего 2 класса в иерархии... – ampawd Nov 06 '21 at 17:04
  • "На практике да, если B не добавляет новых полей с непустыми деструкторами, то ничего не должно сломаться". В принципе эта часть ответа более интересует. Так не должно или точно никакого UB не будет в таком случаи? – AR Hovsepyan Nov 06 '21 at 17:22
  • @ARHovsepyan По стандарту UB будет в любом случае. – HolyBlackCat Nov 06 '21 at 17:23
  • @ampawd Перед ним указатель на виртуальную таблицу. – HolyBlackCat Nov 06 '21 at 17:24
  • 1
    @HolyBlackCat, спасибо, тут у нас с сыном был спор по этому поводу:):) – AR Hovsepyan Nov 06 '21 at 17:25