# V599. The virtual destructor is not present, although the 'Foo' class contains virtual functions.

Анализатор обнаружил потенциальную ошибку, связанную с отсутствием в базовом классе виртуального деструктора.

Чтобы анализатор выдал предупреждение V599 необходимо, чтобы выполнились следующие условия:

- Объект класса уничтожается с помощью оператора `delete`.
- Класс имеет хотя бы одну виртуальную функцию.

Наличие виртуальных функций говорит о том, что класс могут использовать полиморфно. В этом случае виртуальный деструктор необходим, чтобы корректно разрушить объект.

Рассмотрим пример кода.

    class Father
    {
    public:
      Father() { .... }
      ~Father() { .... }
      virtual void Foo() { .... }
    };

    class Son : public Father
    {
    public:
      int *buffer;
      Son() : Father() { buffer = new int[1024]; }
      ~Son() { delete[] buffer; }
      virtual void Foo() { .... }
    };

    ....
    Father *object = new Son();
    delete object;              // Calls object->~Father(),
                                // not object->~Son()!!

Нижеприведённый код является некорректным и приводит к утечке памяти. В момент уничтожения объекта (`delete object;`) вызывается только деструктор в классе `Father`. Чтобы вызвать деструктор класса 'Son' необходимо сделать деструктор виртуальным.

Корректный код:

    class Father
    {
    public:
      Father() { .... }
      virtual ~Father() { .... }
      virtual void Foo() { .... }
    };

Диагностическое правило V599 помогает выявить далеко не все проблемы, связанные с отсутствием виртуальных деструкторов. Приведем соответствующий пример: "Вы разрабатываете библиотеку. В ней есть класс XXX, в котором есть виртуальные функции, но нет виртуального деструктора. Вы в библиотеке сами не работаете с этим классом, и анализатор не предупредит об опасности. Проблема может возникнуть у программиста, использующего вашу библиотеку и наследующего свои классы от класса XXX."

Намного больше проблем позволяют выявлять предупреждения компиляторов, например, `C4265` в MSVC и `Wdelete-non-virtual-dtor` в GCC/Clang. Это очень полезные предупреждения, но по умолчанию они выключены. Возможная причина: они дают много срабатываний в коде, где используется паттерн примесь (подмешивание, mixin). При использовании этого паттерна возникает множество интерфейсных классов. Они содержат виртуальные функции, но виртуальный деструктор в них не нужен.

Можно сказать, что диагностическое правило V599 является частным случаем описанных ранее предупреждений компиляторов. Оно дает меньше ложных срабатываний, но, к сожалению, позволяет выявить меньшее количество дефектов. Если вы хотите провести более тщательный анализ своего кода, то включите предупреждение C4265.

P.S. К сожалению, всегда объявлять деструктор виртуальным не является идеальной практикой программирования. Это приводит к дополнительным накладным расходам, так как в классе появляется указатель на таблицу виртуальных методов.

P.P.S. Родственным диагностическим правилом является V689.

Дополнительная информация:

- Wikipedia. Таблица виртуальных методов.
- Wikipedia. Виртуальный метод (виртуальная функция).
- Wikipedia. Деструктор.
- Сергей Олендаренко. Виртуальные функции и деструктор.

Выявляемые диагностикой ошибки классифицируются согласно ГОСТ Р 71207–2024 как критические и относятся к типу: Ошибки управления динамической памятью (выделения, освобождения, использования освобожденной памяти) [* см. примечание касательно языков C#, Java], Ошибки утечек памяти, незакрытых файловых дескрипторов и дескрипторов сетевых соединений [* см. примечание касательно языков C#, Java].

Данная диагностика классифицируется как:

CWE-1079 CERT-OOP52-CPP

Взгляните на примеры ошибок, обнаруженных с помощью диагностики V599.