关于c ++:GNU编译器警告“类具有虚函数但非虚析构函数”

GNU compiler warning “class has virtual functions but non-virtual destructor”

我在C++中定义了一个接口,即只包含纯虚函数的类。

我想明确禁止接口用户通过指向接口的指针删除对象,因此我为接口声明了一个受保护的非虚拟析构函数,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
class ITest{
public:
    virtual void doSomething() = 0;

protected:
    ~ITest(){}
};

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

GNU编译器警告我说:

class 'ITest' has virtual functions but non-virtual destructor

一旦析构函数受到保护,让它虚拟化或非虚拟化有什么区别?

你认为这个警告可以被安全地忽略或沉默吗?


这或多或少是编译器中的一个错误。请注意,在最新版本的编译器中,不会引发此警告(至少在4.3中不会)。在您的情况下,保护析构函数和非虚拟析构函数是完全合法的。

Herb Sutter关于这一主题的优秀文章见这里。从文章中:

准则4:基类析构函数应该是公共的和虚拟的,或者是受保护的和非虚拟的。


关于这个答案的一些评论与我之前给出的答案有关,这是错误的。

受保护的析构函数意味着它只能从基类调用,而不能通过删除来调用。这意味着不能直接删除ITest*,只有派生类才能删除。派生类很可能需要一个虚拟析构函数。你的代码完全没有问题。

但是,由于您不能在GCC中本地禁用警告,并且您已经有了vtable,所以您可以考虑将析构函数设置为虚拟的。这个程序最多需要4个字节(不是每个类实例)。因为您可能已经给派生类一个虚拟的DTOR,所以您可能会发现它不会花费任何代价。


如果你坚持这样做,就把-Wno-non-virtual-dtor交给GCC。此警告在默认情况下似乎没有打开,因此您必须使用-Wall-Weffc++启用它。但是,我认为这是一个有用的警告,因为在大多数情况下,这是一个bug。


我个人的观点是你做了正确的事情,编译器就坏了。如果可能的话,我将禁用警告(在定义接口的文件中的本地部分),

我发现我经常使用这种模式(小P)。事实上,我发现我的接口保护数据终端比保护公共数据终端更常见。不过,我不认为这是一个常见的习惯用法(人们不会这么多谈论它),我想当警告添加到gcc时,如果您有虚拟函数规则,那么尝试和强制使用旧的"dtor must be virtual"是合适的。我个人将该规则更新为"如果您具有虚拟功能,并且希望用户能够通过接口删除接口的实例,则DTOR必须是虚拟的",否则DTOR应该受到保护,而非"虚拟的"年代之前;)


它是一个接口类,所以您不应该删除通过该接口实现该接口的对象是合理的。常见的情况是工厂创建的对象的接口,这些对象应返回工厂。(让对象包含指向其工厂的指针可能非常昂贵)。

我同意GCC抱怨的说法。相反,当您删除ITest*时,它应该只发出警告。那才是真正的危险所在。


如果您在ITest的方法中有一个尝试delete本身的代码(一个坏主意,但合法),则不会调用派生类的析构函数。即使从未打算通过基类指针删除派生实例,也应该使析构函数成为虚拟的。


如果析构函数是虚拟的,那么在进行清理之前,它会确保也会调用基类析构函数,否则代码可能会导致一些泄漏。因此,您应该确保程序没有这样的警告(很可能根本没有警告)。