Класс с виртуальной функцией должен иметь виртуальный или защищённый деструктор
Причина
Класс с виртуальной функцией обычно (и в целом) используется через указатель на базу. Обычно последний пользователь должен вызвать delete на указателе на базу, часто через умный указатель на базу, так что деструктор должен быть public и виртуальным. Реже, если удаление через указатель на базу не предполагается поддерживать, деструктор должен быть protected и невиртуальным; см. C.35.
Плохой пример
struct B {
virtual int f() = 0;
// ... no user-written destructor, defaults to public non-virtual ...
};
// bad: derived from a class without a virtual destructor
struct D : B {
string s {"default"};
// ...
};
void use()
{
unique_ptr<B> p = make_unique<D>();
// ...
} // undefined behavior, might call B::~B only and leak the string
Примечание
Есть люди, которые не следуют этому правилу, потому что они планируют использовать класс только через shared_ptr: std::shared_ptr<B> p = std::make_shared<D>(args); Здесь общий указатель позаботится об удалении, поэтому утечка не произойдёт от неправильного delete базы. Люди, которые делают это последовательно, могут получить ложное срабатывание, но правило важно — что если один был выделен с помощью make_unique? Это не безопасно, если автор B не гарантирует, что он никогда не может быть неправильно использован, например, делая все конструкторы приватными и предоставляя функцию фабрики для обеспечения выделения с make_shared.
Применение
- Класс с любыми виртуальными функциями должен иметь деструктор, который либо public и виртуальный, либо protected и невиртуальный.
- Отмечайте
deleteкласса с виртуальной функцией, но без виртуального деструктора.