Предупреждение компилятора GNU: «У класса есть виртуальные функции, но невиртуальный деструктор»

StackOverflow https://stackoverflow.com/questions/127426

  •  02-07-2019
  •  | 
  •  

Вопрос

Я определил интерфейс на 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 имеет виртуальные функции, но невиртуальный деструктор

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

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

Это было полезно?

Решение

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

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

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

Другие советы

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top