#c #virtual #destructor
#c #виртуальный #деструктор
Вопрос:
class Base {
public:
virtual ~Base () { cout << "Base Dtor" << endl; }
virtual void doSomething () { cout << "Base do something" << endl; }
};
class Derive : public Base {
public:
~Derive () { cout << "Derive Dtor" << endl; }
void doSomething () { cout << "Derive do something" << endl; }
};
когда я пытаюсь написать какой-то код, например:
Base* ptr = new Derive;
ptr->doSomething ();
delete ptr;
функция doSomething()
вызывается правильно. Почему вызывается производный деструктор, а затем вызывается базовый деструктор? Но doSomething()
просто вызывайте только версию производного класса. Деструктор и doSomething()
тоже функция, но почему они ведут себя по-разному?
Комментарии:
1. Потому что это то, что должны делать деструкторы: уничтожать весь объект, что делается путем вызова деструктора базового класса.
2. Вызов деструктора через указатель базового класса статически вызовет базовый деструктор, если базовый деструктор не
virtual
является. Но если базовый деструктор естьvirtual
, то производный деструктор вызывается полиморфно, как и любой другой виртуальный метод. Однако, как и конструкторы, деструкторы отличаются тем, что они всегда вызывают базу автоматически.
Ответ №1:
Деструкторы объектов являются особыми. Да, это функции, но на самом деле они представляют что-то конкретное. А именно, уничтожение объекта.
В вашем примере Derive
это объект. Но через наследование Derive
вызывается подобъект базового класса Base
. То есть для каждого Derive
Base
в этом объекте также есть жизнь Derive
.
Когда вы вызываете конструктор для создания a Derive
, соответствующий конструктор будет вызван и для Base
подобъекта (в зависимости от того, как вы определили свой Derive
конструктор). Таким образом, если вы уничтожаете a Derive
, Base
живое внутри него также должно быть уничтожено.
virtual
Вызов деструктора на самом деле этого не меняет. То, что он virtual
делает, позволяет системе правильно запускать цепочку деструкторов. Если бы вы не объявили Base
деструктор virtual
, то вызов delete
указателя базового класса вызывал бы только ~Base
. Он не сможет перейти к графу наследования для вызова ~Derive
; delete
выражению присваивается Base
указатель на удаление, так что это то, что он делает.
Это тот же механизм, что и любой virtual
вызов. Если вы не создали обычную функцию virtual
-член и вызываете ее по указателю Base
класса, вы получаете Base
ее версию, даже если существует Derive
версия с тем же именем. Вам нужно virtual
заставить компилятор перейти к графу наследования и найти правильную функцию для вызова.
Но как только компилятор узнает правильный тип объекта для запуска цепочки деструкторов, эта цепочка уничтожения объектов все равно произойдет. Derive
содержит a Base
, поэтому для Derive
уничтожения a его Base
также необходимо уничтожить.
Ответ №2:
Почему вызывается производный деструктор, а затем вызывается базовый деструктор?
Потому что деструктор всегда уничтожает все вложенные объекты. Это включает в себя основы.
Ответ №3:
Вы переопределили doSomething
функцию, поэтому она выполняется только для Derive
объявленного вами объекта.
Вы можете видеть, что деструкторы имеют разные имена. Когда вы вызываете деструктор какого-либо объекта, автоматически вызывается и деструктор отца.