GNU编译器警告"类具有虚拟功能但非虚拟析构函数"

时间:2020-03-06 14:39:54  来源:igfitidea点击:

我已经在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编译器给我警告:

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

一旦析构函数受到保护,将其虚拟化或者非虚拟化有何区别?

我们认为可以安全地忽略或者消除此警告吗?

解决方案

如果析构函数是虚拟的,则请确保也要先进行基类析构函数的调用,以免进行清理,否则该代码可能导致某些泄漏。因此,我们应确保该程序没有此类警告(以前根本没有任何警告)。

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

受保护的析构函数意味着只能从基类中调用它,而不能通过delete进行调用。这意味着不能直接删除ITest *,只能删除派生类。派生类可能需要虚拟析构函数。代码完全没有错。

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

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

它或者多或者少是编译器中的错误。请注意,在较新版本的编译器中,不会引发此警告(至少在4.3中不会)。在情况下,使析构函数受到保护且非虚拟是完全合法的。

请参阅此处,获取Herb Sutter撰写的关于该主题的出色文章。从文章:

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

如果我们坚持要这样做,请继续将-Wno-non-virtual-dtor传递给GCC。该警告似乎默认情况下未启用,因此我们必须使用-Wall或者-Weffc ++启用它。但是,我认为这是一个有用的警告,因为在大多数情况下,这将是一个错误。

这是一个接口类,因此我们不应该删除通过该接口实现该接口的对象,这是合理的。常见的情况是工厂创建的对象的接口,应将其返回工厂。 (使对象包含指向其工厂的指针可能会非常昂贵)。

我同意海湾合作委员会在抱怨的观点。相反,它应该在删除ITest *时简单地发出警告。那才是真正的危险所在。

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

我发现我经常使用这种模式(小" p")。实际上,我发现接口具有受保护的dtor比具有公共接口的更常见。但是我不认为这实际上是一个惯用的习惯用法(它没有被提及太多),而且我回想起在将警告添加到GCC时,尝试强制使用旧的'dtor必须是虚拟的,如果有虚拟功能的规则。我个人将规则更新为"如果我们具有虚拟功能,并且必须希望用户能够通过该接口删除该接口的实例,则dtor必须是虚拟的,否则dtor应该受到保护并且是非虚拟的";