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,所以您可能会发现它不会花费任何代价。
如果你坚持这样做,就把
我个人的观点是你做了正确的事情,编译器就坏了。如果可能的话,我将禁用警告(在定义接口的文件中的本地部分),
我发现我经常使用这种模式(小P)。事实上,我发现我的接口保护数据终端比保护公共数据终端更常见。不过,我不认为这是一个常见的习惯用法(人们不会这么多谈论它),我想当警告添加到gcc时,如果您有虚拟函数规则,那么尝试和强制使用旧的"dtor must be virtual"是合适的。我个人将该规则更新为"如果您具有虚拟功能,并且希望用户能够通过接口删除接口的实例,则DTOR必须是虚拟的",否则DTOR应该受到保护,而非"虚拟的"年代之前;)
它是一个接口类,所以您不应该删除通过该接口实现该接口的对象是合理的。常见的情况是工厂创建的对象的接口,这些对象应返回工厂。(让对象包含指向其工厂的指针可能非常昂贵)。
我同意GCC抱怨的说法。相反,当您删除ITest*时,它应该只发出警告。那才是真正的危险所在。
如果您在
如果析构函数是虚拟的,那么在进行清理之前,它会确保也会调用基类析构函数,否则代码可能会导致一些泄漏。因此,您应该确保程序没有这样的警告(很可能根本没有警告)。