Виртуальные деструкторы для интерфейсов
нужен ли интерфейсам виртуальный деструктор,или автоматически сгенерированный? Например, какое из следующих двух фрагментов кода лучше, и почему? Обратите внимание, что это весь класс. Нет других методов, переменных и т. д. В Java-speak это "интерфейс".
class Base
{
public:
virtual void foo() = 0;
virtual ~Base() {}
};
или...
class Base
{
public:
virtual void foo() = 0;
~Base() {} // This line can be omitted, but included for clarity.
};
РЕДАКТИРОВАТЬ ИЗ-ЗА" НЕ ТО, ЧТО Я ИЩУ " ОТВЕТЫ:
каковы именно последствия каждого маршрута. Пожалуйста, не дают неопределенные ответы, такие как"он не будет уничтожен должным образом". Пожалуйста, скажите мне, что именно произойдет. Я немного помешан на собраниях.
Edit 2:
Я хорошо знаю, что" виртуальный " тег означает, что деструктор не будет вызываться, если он будет удален через указатель на производный, но (я думаю) этот вопрос в конечном итоге сводится к "безопасно ли опустить этот деструктор, ибо это действительно тривиально?"
EDIT 3:
мое второе редактирование просто ложь и дезинформация. Пожалуйста, прочитайте комментарии фактических умных людей для получения дополнительной информации.
6 ответов
рассмотрим следующий случай:
Base *Var = new Derived();
delete Var;
вам нужен виртуальный деструктор, иначе при удалении Var
, деструктор производного класса не будет называться.
Если вы удалите объект производного класса с помощью указателя базового класса В C++, результатом будет неопределенное поведение. UB-это то, чего вы действительно хотите избежать, поэтому вы должны дать базовым классам виртуальный деструктор. Цитата из стандарта C++, раздел 5.3.5:
Если статический тип операнда отличающийся от свой динамический тип, статический тип является базовым классом динамический тип операнда и статический тип должен иметь виртуальный деструктор или поведение не определено.
вы должны использовать виртуальный деструктор, если ожидаете, что люди попытаются удалить объекты производного класса с помощью указателей или ссылок родительского класса. Если это так, то без виртуального деструктора производный класс никогда не будет правильно уничтожен.
например,
Derived::~Derived() { // important stuff }
Base *foo = new Derived();
delete foo;
без виртуального деструктора в базе деструктор Derived никогда не будет вызван, и поэтому важные вещи никогда не произойдут.
отвечая в основном для редактирования:
никто не может сказать вам, что произойдет, потому что в результате "неопределенное поведение". При удалении производного класса с помощью указателя на базу, не имеющую виртуального деструктора, реализация может быть разрушена любым количеством способов.
В общем случае деструктор должен быть либо (1) публичный и виртуальный, или (2) защищенный и не виртуальный.
предполагая, что вы никогда не ожидаете, что кто-то удалит экземпляр класса с помощью указателя интерфейса, защищенный не виртуальный деструктор на 100% безопасен.
Если кто-то попытается удалить указатель интерфейса в случае (2), они получат ошибку времени компиляции.
нет... виртуальные деструкторы не генерируются автоматически. Вы должны объявить их эксплицитно в своем базовом классе. Но вам не нужно будет объявлять ваши деструкторы виртуальными для дочерних классов базы. Это делается компилятором. Компилятор также гарантирует, что деструкторы вызываются в обратном порядке построения (от производного до базового).
public class Base
{
//...
}
public class Derived
{
int i = 0;
//...
}
//...
Base* b = new Derived();
если у вас нет виртуального деструктора
delete b;
приведет к утечке памяти (не менее 4 байт для целочисленного поля), потому что он уничтожит только Base
, а не Derived
. Виртуальность гарантирует, что производные классы также будут уничтожены. Вам не придется объявлять виртуальный конструктор в Derived
, это будет выведено компилятором, если вы объявили виртуальный деструктор в Base
.