59

我在 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

7 回答 7

66

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

请参阅此处查看 Herb Sutter 撰写的关于该主题的优秀文章。来自文章:

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

于 2008-09-24T14:26:09.317 回答
9

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

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

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

于 2008-09-24T14:21:48.047 回答
4

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

于 2008-09-24T14:37:01.490 回答
2

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

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

于 2008-09-24T14:39:17.973 回答
2

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

我发现我经常使用这种模式(小“p”)。事实上,我发现我的接口拥有受保护的 dtor 比拥有公共的更常见。但是我不认为它实际上是一个常见的习语(它并没有被谈论那么多),我猜想当警告被添加到 GCC 时,尝试强制执行旧的 'dtor 必须是虚拟的,如果你有虚函数的规则。就我个人而言,我将该规则更新为“如果您具有虚拟功能并希望用户能够通过接口删除接口的实例,则 dtor 必须是虚拟的,否则 dtor 应该受到保护并且是非虚拟的”;)

于 2008-09-24T14:40:07.787 回答
0

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

于 2008-09-24T14:19:41.313 回答
0

如果您在ITest's 方法之一中尝试了delete自己的代码(这是一个坏主意,但合法),则不会调用派生类的析构函数。即使您从不打算通过基类指针删除派生实例,您仍然应该使您的析构函数成为虚拟的。

于 2008-09-24T14:23:17.577 回答