Во-первых, я понимаю, почему деструкторы virtual
необходимы в терминах единого наследования и удаления объекта с помощью базового указателя. Это особенно касается множественного наследования и причины, почему это работает. Этот вопрос возник в одном из моих университетских классов, и никто (включая профессора) не был уверен, почему это сработало:
#include <iostream>
struct A
{
virtual ~A()
{
std::cout << "~A" << std::endl;
}
int memberA;
};
struct B
{
virtual ~B()
{
std::cout << "~B" << std::endl;
}
int memberB;
};
struct AB : public A, public B
{
virtual ~AB()
{
std::cout << "~AB" << std::endl;
}
};
int main()
{
AB* ab1 = new AB();
AB* ab2 = new AB();
A* a = ab1;
B* b = ab2;
delete a;
delete b;
}
Выход для этого:
~AB
~B
~A
~AB
~B
~A
Как компилятор знает, как вызвать A
и B
деструктор при удалении A
или B
? В частности, как выкладывается память для AB
(в частности, таблица виртуальных функций), так что деструкторы A
и B
могут быть вызваны?
Мой профессор предположил, что память будет выложена (что-то вроде этого):
AB
+---------+ +----+
| A VFT | - - - - - -> | ~A |
+---------+ +----+
| memberA |
+---------+ +----+
| B VFT | - - - - - -> | ~B |
+---------+ +----+
| memberB |
+---------+
// I have no idea where ~AB would go...
Нам всем любопытно, как эти деструкторы фактически выложены в памяти и как вызов delete
на любом из A
или B
приводит к тому, что все деструкторы будут правильно вызваны. Имеет смысл, что удаление базового объекта работает в одиночном наследовании (потому что есть одна таблица виртуальных функций для работы), но, видимо, я неправильно понимаю вещи, потому что я не могу понять свое единственное наследственное исполнение и применить его к этому примеру множественного наследования.
Итак, как это работает?