GNU compiler warning " класс имеет виртуальные функции, но не виртуальный деструктор"


Я определил интерфейс в C++, т. е. класс, содержащий только чисто виртуальные функции.

Я хочу явно запретить пользователям интерфейса удалять объект через указатель на интерфейс, поэтому я объявил защищенный и невиртуальный деструктор для интерфейса, что-то вроде:

class ITest{
public:
    virtual void doSomething() = 0;

protected:
    ~ITest(){}
};

void someFunction(ITest * test){
    test->doSomething(); // ok
    // deleting object is not allowed
    // delete test; 
}

компилятор GNU дает мне предупреждение:

класс 'ITest' имеет виртуальные функции, но не виртуальные деструктор

Как только деструктор защищен, какая разница, виртуальный он или не виртуальный?

Как вы думаете, это предупреждение можно проигнорировать или замолчать?

7 57

7 ответов:

Это более или менее ошибка в компиляторе. Обратите внимание, что в более поздних версиях компилятора это предупреждение не бросают (по крайней мере в 4.3 это не так). Если деструктор защищен и не является виртуальным, это полностью законно в вашем случае.

посмотреть здесь за отличную статью Херба Саттера на эту тему. Из статьи:

руководство №4: деструктор базового класса должен быть либо общедоступным и виртуальным, либо защищенным и невиртуальный.

некоторые комментарии к этому ответу относятся к более раннему ответу, который я дал, что было неправильно.

защищенный деструктор означает, что он может быть вызван только из базового класса, а не через delete. Это означает, что ITest* не может быть удален напрямую, только производный класс может. Производный класс вполне может нуждаться в виртуальном деструкторе. Нет ничего плохого в вашем коде вообще.

однако, поскольку вы не можете локально отключить предупреждение в GCC, и у вас уже есть vtable, вы могли бы просто сделать деструктор виртуальным в любом случае. Это будет стоить вам 4 байта для программы (не на экземпляр класса), максимум. Поскольку вы, возможно, дали своему производному классу виртуальный dtor, вы можете обнаружить, что это ничего не стоит.

Если вы настаиваете на этом, идите вперед и проходите -Wno-non-virtual-dtor в GCC. Это предупреждение, похоже, не включено по умолчанию, поэтому вы должны были включить его с помощью -Wall или -Weffc++. Тем не менее, я думаю, что это полезное предупреждение, потому что в большинстве ситуаций это будет ошибка.

Это класс интерфейса, поэтому разумно не удалять объекты, реализующие этот интерфейс через этот интерфейс. Общим случаем этого является интерфейс для объектов, созданных фабрикой, которые должны быть возвращены на фабрику. (Наличие объектов, содержащих указатель на их фабрику, может быть довольно дорогим).

Я бы согласился с наблюдением, что GCC скулит. Вместо этого он должен просто предупреждать, когда вы удаляете ITest*. Вот где кроется настоящая опасность.

мое личное мнение заключается в том, что вы делаете правильную вещь, и компилятор сломан. Я бы отключил предупреждение (локально в файле, который определяет интерфейс) , если это возможно,

Я считаю, что я использую этот шаблон (маленький 'p') довольно много. На самом деле я считаю, что для моих интерфейсов более распространено иметь защищенные dtors, чем для них иметь публичные. Однако я не думаю, что на самом деле это обычная идиома (об этом не говорят так много), и я думаю, что когда предупреждение было добавлено в GCC было целесообразно попытаться применить более старое правило "dtor должен быть виртуальным, если у вас есть виртуальные функции". Лично я обновил это правило до "dtor должен быть виртуальным, если у вас есть виртуальные функции и вы хотите, чтобы пользователи могли удалять экземпляры интерфейса через интерфейс, иначе dtor должен быть защищен и не виртуальным" много лет назад;)

Если деструктор является виртуальным, он гарантирует, что деструктор базового класса также вызывается перед выполнением очистки, иначе некоторые утечки могут возникнуть из этого кода. Поэтому вы должны убедиться, что программа не имеет таких предупреждений (предпочтительно никаких предупреждений вообще).

Если у вас был код в один из ITestметоды, которые пытались delete сам (плохая идея, но законная), деструктор производного класса не будет вызван. Вы все равно должны сделать свой деструктор виртуальным, даже если вы никогда не собираетесь удалять производный экземпляр с помощью указателя базового класса.