Деструктор базового класса должен быть либо публичным и виртуальным, либо защищённым и невиртуальным
Причина
Для предотвращения неопределённого поведения. Если деструктор публичный, вызывающий код может попытаться уничтожить объект производного класса через указатель на базовый класс, и результат будет неопределён, если деструктор базового класса невиртуальный. Если деструктор защищённый, вызывающий код не может уничтожить объект через указатель на базовый класс, и деструктор не обязан быть виртуальным; он должен быть защищённым, а не приватным, чтобы деструкторы производных классов могли его вызывать. В общем случае автор базового класса не знает, какое действие следует выполнить при уничтожении.
Обсуждение
Смотрите это в разделе Обсуждение.
Пример (плохой)
struct Base { // ПЛОХО: неявно имеет публичный невиртуальный деструктор
virtual void f();
};
struct D : Base {
string s {"ресурс, требующий очистки"};
~D() { /* ... выполняем очистку ... */ }
// ...
};
void use()
{
unique_ptr<Base> p = make_unique<D>();
// ...
} // уничтожение p вызывает ~Base(), а не ~D(), что приводит к утечке D::s и, возможно, большего
Примечание
Виртуальная функция определяет интерфейс для производных классов, который можно использовать без просмотра производных классов. Если интерфейс допускает уничтожение, это должно быть безопасным.
Примечание
Деструктор не должен быть приватным, иначе будет невозможно использовать тип:
class X {
~X(); // приватный деструктор
// ...
};
void use()
{
X a; // ошибка: нельзя уничтожить
auto p = make_unique<X>(); // ошибка: нельзя уничтожить
}
Исключение
Можно представить один случай, когда вам понадобится защищённый виртуальный деструктор: когда объект производного типа (и только такого типа) должен иметь возможность уничтожить другой объект (не себя) через указатель на базу. На практике мы не встречали такого случая.
Контроль
- Класс с любыми виртуальными функциями должен иметь деструктор, который является либо публичным и виртуальным, либо защищённым и невиртуальным.
- Если класс публично наследует от базового класса, базовый класс должен иметь деструктор, который является либо публичным и виртуальным, либо защищённым и невиртуальным.