У меня есть иерархия классов, которая имеет три уровня, например:
class A {
public:
virtual ~A() {}
}
class B : public A {
public:
virtual ~B() {}
void foo(E *e) {
e->remove(this);
}
}
class C : public B {
public:
~C() {}
}
class E {
public:
void remove(A *a) {
delete a;
}
}
Хорошо, так что мне интересно, что происходит, когда я звоню foo()
на объекте C
, Это собирается удалить весь объект или только B
а также A
часть объекта, и оставить C
часть еще в памяти?
Это собирается удалить весь объект или только части B и A объекта и оставить часть C все еще в памяти?
Нет. Он будет «делать правильные вещи» (то есть удалять наиболее производный подобъект, запускать все его деструкторы и т. Д.) При условии A
(то есть статический тип указателя указателя, который вы delete
) имеет виртуальный деструктор (и если класс A
есть виртуальный деструктор, он есть и у всех его потомков). Это относится и к множественному наследованию.
Благодаря виртуальному деструктору в A
код будет работать правильно уничтожить весь экземпляр C
,
delete
всегда освобождает память для всего объекта, на который указывает
Однако это не относится к вызываемым деструкторам: он попытается вызвать деструктор статического типа удаляемого объекта.
В полиморфном сценарии это часто не то, что вы хотите. Учти это:
struct Base { };
struct Derived : Base
{
int* i;
Derived() : i(new int) { }
~Derived() { delete i; }
}
void bad()
{
Base* b = new Derived;
delete b;
}
bad()
вызывает утечку памяти, потому что Derived
Деструктор никогда не будет вызван. Это потому, что статический тип б Base*
таким образом Base::~Base
будет называться. Там нет деструктора, определенного в Base
Таким образом, выполняется реализация по умолчанию, предоставляемая компилятором, которая ничего не делает в этом конкретном примере.
Это, однако, не относится к вашему примеру: вы сделали виртуальный деструктор корневого класса, поэтому деструкторы всех производных классов будут выполняться как часть вызова деструктора.