GNU编译器警告“类具有虚拟函数但非虚拟析构函数”
我已经在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”具有虚拟function但是非虚拟析构函数
一旦析构函数被保护,它有什么区别在虚拟或非虚拟?
你认为这个警告可以被安全地忽略或沉默吗?
这或多或less是编译器中的一个bug。 请注意,在更新的编译器版本中,这个警告不会被抛出(至less在4.3中不会)。 让析构函数得到保护,并且非虚拟的在你的情况下是完全合法的。
在这里看到Herb Sutter关于这个问题的出色文章。 从文章:
准则4:基类析构函数应该是public和virtual,或者是protected和nonvirtual。
关于这个答案的一些评论涉及到我给的早期答案,这是错误的。
受保护的析构函数意味着它只能从基类中调用,而不能通过删除。 这意味着ITest *不能直接删除,只有派生类才可以。 派生类可能需要一个虚拟析构函数。 你的代码没有任何问题。
然而,既然你不能在本地禁用GCC中的警告,并且你已经有了一个vtable,你可以考虑只是让析构函数变成虚拟的。 它会花费你4个字节的程序(不是每个类的实例),最大。 既然你可能已经给你的派生类一个虚拟的Dtor,你可能会发现它没有花费任何东西。
如果你坚持这样做的话, -Wno-non-virtual-dtor
传给GCC。 这个警告似乎没有默认打开,所以你必须使用-Wall
或-Weffc++
启用它。 不过,我认为这是一个有用的警告,因为在大多数情况下,这将是一个错误。
这是一个接口类,所以你不应该通过该接口删除实现该接口的对象。 一个常见的情况是由工厂创build的对象的接口应该被返回到工厂。 (让对象包含指向他们工厂的指针可能会相当昂贵)。
我同意GCC发牢骚的观察。 相反,它应该只是警告当你删除一个ITest *。 这就是真正的危险所在。
我个人的观点是,你会做正确的事情,编译器坏了。 如果可能的话,我会禁用警告(本地在定义接口的文件中)
我发现我使用这种模式(小'p')相当多。 事实上,我发现我的接口具有受保护的dtors比拥有公共接口更常见。 然而,我不认为这是一个普通的习惯用语(它不会说得那么多),当我向GCC添加警告时,我猜想回来的时候,尝试和强制执行老的“如果你有虚拟function“的规则。 就我个人而言,我更新了这个规则,“如果你有虚拟function必须是虚拟的,并且希望用户能够通过界面删除界面的实例,否则应该保护虚拟桌面,并且非常虚拟”);
如果析构函数是虚拟的,它确保基类析构函数也被称为前清理,否则可能导致一些泄漏。 所以你应该确保程序没有这样的警告(完全没有警告)。
如果你有一个ITest
的方法试图delete
自己(坏主意,但合法)的代码,派生类的析构函数将不会被调用。 即使你不打算通过基类指针删除派生实例,你仍然应该使你的析构函数为虚拟的。