Интерфейсам нужен виртуальный деструктор, или действительно ли автоматически сгенерированный прекрасен? Например, какой из следующих двух фрагментов кода является лучшим, и почему? Обратите внимание на то, что это ЦЕЛЫЙ класс. Нет никаких других методов, переменные, и т.д. В Java - говорят, это - "интерфейс".
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.
};
ОТРЕДАКТИРУЙТЕ ИЗ-ЗА "НЕ, ЧТО Я ИЩУ" ОТВЕТЫ:
Точно, каковы последствия каждого маршрута. Не давайте неопределенные ответы как "он, не будет разрушен правильно". Скажите мне точно, что произойдет. Я - немного компьютерный фанат блока.
Редактирование 2:
Я хорошо знаю, что "виртуальный" тег означает, что деструктор не назовут, если удалено через указатель на полученный, но (я думаю), этот вопрос в конечном счете сводится к "ему, безопасно опустить тот деструктор, поскольку действительно ли это действительно тривиально?"
РЕДАКТИРОВАНИЕ 3:
Мое второе редактирование является просто неправильным и дезинформация. Прочитайте комментарии фактических умных людей для большего количества информации.
Рассмотрим следующий случай:
Base *Var = new Derived();
delete Var;
Вам нужен виртуальный деструктор, иначе при удалении Var
деструктор производного класса никогда не будет вызван.
Ответ в основном на правку:
Никто не может сказать вам, что произойдет, потому что результатом является "неопределенное поведение". Когда вы удаляете производный класс с помощью указателя на базу, у которой нет виртуального деструктора, реализация может быть разрушена любым количеством способов.
Обычно деструктор должен быть либо (1) общедоступным и виртуальным, либо (2) защищенным и не виртуальным.
Предполагая, что вы никогда не ожидаете, что кто-то удалит экземпляр класса через указатель интерфейса, защищенный невиртуальный деструктор на 100% безопасен.
Если кто-то попытается удалить указатель интерфейса в случае (2), он получит ошибку времени компиляции.
Вам следует использовать виртуальный деструктор, если вы ожидаете, что люди попытаются удалить объекты производного класса с помощью указателей или ссылок на родительский класс. Если это так, то без виртуального деструктора производный класс никогда не будет должным образом уничтожен.
Например,
Derived::~Derived() { // important stuff }
Base *foo = new Derived();
delete foo;
Без виртуального деструктора в Base деструктор Derived никогда не будет вызван, и поэтому никогда не произойдет важных вещей.
Нет ... виртуальные деструкторы не генерируются автоматически. Вы должны явно объявить их в своем базовом классе. Но вам не нужно объявлять свои деструкторы виртуальными для дочерних классов Base. Это делает компилятор. Компилятор также позаботится о том, чтобы деструкторы вызывались в обратном порядке построения (от производного к основному).
public class Base
{
//...
}
public class Derived
{
int i = 0;
//...
}
//...
Base* b = new Derived();
Если у вас нет виртуального деструктора
delete b;
, это вызовет утечку памяти (по крайней мере 4 байта для целочисленного поля), потому что он разрушит только Base
, а не Derived
]. Виртуальность гарантирует, что производные классы также будут уничтожены. Вам не нужно объявлять виртуальный конструктор в Derived
, это будет сделано компилятором, если вы объявили виртуальный деструктор в Base
.
Если вы удалите объект производного класса с помощью указателя базового класса в C ++, результатом будет неопределенное поведение. UB - это то, чего вы действительно хотите избежать, поэтому вы должны предоставить базовым классам виртуальный деструктор. Процитируем стандарт C ++, раздел 5.3.5:
, если статическим типом операнда является отличается от своего динамического типа, статический тип должен быть базовым классом динамический тип операнда и статический тип должен иметь виртуальный деструктор или поведение неопределенный.