Виртуальные деструкторы для интерфейсов

нужен ли интерфейсам виртуальный деструктор,или автоматически сгенерированный? Например, какое из следующих двух фрагментов кода лучше, и почему? Обратите внимание, что это весь класс. Нет других методов, переменных и т. д. В 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.