← Назад к вопросам
Зачем нужен виртуальный деструктор?
1.0 Junior🔥 301 комментариев
#ООП и проектирование#Язык C++
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Зачем нужен виртуальный деструктор
Виртуальный деструктор — это критическое требование для полиморфных классов, обеспечивающее корректное удаление объектов через базовый указатель. Без него программа страдает от утечек памяти и корупции памяти.
Проблема: деструктор без virtual
Когда производный класс наследует базовый без virtual деструктора:
class Base {
public:
~Base() { std::cout << "Base destructor\n"; }
};
class Derived : public Base {
private:
std::string buffer; // Требует очистки
public:
~Derived() {
std::cout << "Derived destructor\n";
// buffer автоматически очистится
}
};
Base* ptr = new Derived();
delete ptr; // ПРОБЛЕМА!
Что происходит:
- Вызывается только ~Base()
- Деструктор Derived() НЕ вызывается
- Память buffer не освобождается
- УТЕЧКА ПАМЯТИ
Решение: virtual деструктор
class Base {
public:
virtual ~Base() { std::cout << "Base destructor\n"; }
};
class Derived : public Base {
private:
std::string buffer;
public:
~Derived() override { // override для ясности
std::cout << "Derived destructor\n";
}
};
Base* ptr = new Derived();
delete ptr; // Правильно!
// Вывод:
// Derived destructor
// Base destructor
Как virtual деструктор работает
Виртуальные функции используют VTable (virtual table):
// VTable для Base
struct Base_VTable {
void (*destructor)(Base*);
};
Base* ptr = new Derived();
delete ptr; // Ищет в VTable указателя Derived
// Вызывает Derived::~Derived()
Компилятор автоматически определяет реальный тип объекта в runtime и вызывает правильный деструктор.
Практические сценарии
Интерфейсы и абстрактные классы:
class ILogger {
public:
virtual ~ILogger() = default;
virtual void log(const std::string& msg) = 0;
};
class FileLogger : public ILogger {
private:
std::ofstream file;
public:
~FileLogger() override { file.close(); }
void log(const std::string& msg) override { file << msg; }
};
Полиморфные контейнеры:
std::vector<std::unique_ptr<Base>> objects;
objects.push_back(std::make_unique<Derived1>());
objects.push_back(std::make_unique<Derived2>());
// При уничтожении вектора — каждый деструктор вызовется правильно
С shared_ptr:
std::shared_ptr<Base> ptr = std::make_shared<Derived>();
// При delete — правильно вызовется Derived::~Derived()
Правило: где virtual деструктор обязателен
Используй virtual деструктор если:
- Класс предназначен для наследования
- Есть виртуальные методы
- Класс может быть удален через базовый указатель
Не нужен если:
- Класс final (не наследуется)
- Нет виртуальных методов
- Никогда не удаляется через базовый указатель
Производительность
- Оверхед: один указатель VTable на объект (8 байт на 64-bit)
- При удалении: одна виртуальная диспетчеризация (очень быстро)
- Оптимизирует: компилятор часто инлайнит при final классах
// GCC с -O2 может оптимизировать:
Derived* ptr = new Derived();
delete ptr; // Виртуальный вызов опускается
Виртуальный деструктор — это не опциональный бонус, а обязательная практика для любого полиморфного класса в production коде.