Предупреждение компилятора GNU "класс имеет виртуальные функции, но не виртуальный деструктор" - PullRequest
57 голосов
/ 24 сентября 2008

Я определил интерфейс в 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 ]

65 голосов
/ 24 сентября 2008

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

См. здесь для превосходной статьи Херба Саттера на эту тему. Из статьи:

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

9 голосов
/ 24 сентября 2008

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

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

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

4 голосов
/ 24 сентября 2008

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

2 голосов
/ 24 сентября 2008

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

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

2 голосов
/ 24 сентября 2008

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

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

0 голосов
/ 24 сентября 2008

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

0 голосов
/ 24 сентября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...