如何删除没有虚析构函数的多态类类型的对象

20

我尝试编译一些来自第三方SDK的代码时,出现了以下错误。

*Description    Resource    Path    Location    Type
deleting object of polymorphic class type ‘Vendor_sys::VendorCode’ which has non-virtual destructor might cause undefined behaviour [-Werror=delete-non-virtual-dtor]   PnServer.cpp    /PCounter   line 467    C/C++ Problem*

我不知道是否有可能在只了解厂商SDK的一部分知识的情况下满足这个条件,因为大部分重要的工作都是在dll或库对象中完成的。

我的构建环境是Eclipse Juno和gpp。

我在Google上搜索了错误信息,但没有找到任何此错误的实例。

所以,如果我不能修改供应商代码的黑盒子部分,我的选择是什么?

这里是在编译过程中失败的代码:

delete pData->unit;

2
所以这个类是用来继承的,但是它没有虚析构函数?我会向制作该库的人提出抱怨。 - chris
1
警告有点温和。通过指向没有虚析构函数的基类型的指针删除派生类型的对象会导致未定义的行为。这不是“可能”的问题。 - Pete Becker
@PeteBecker 至少 g++ 4.8 会发出这个警告,即使我通过指向派生类的指针删除。这应该是安全的(据我所知),所以“可能”在这里也是错误的(根本不应该生成警告)。我猜测 g++ 无法区分这两种情况。 - sstn
@PeterBecker 在我的情况下,它当然可以是一个进一步派生的类(在这种情况下警告是正确的),但是我认为这种情况永远不可能被排除。 - sstn
6个回答

18

很抱歉,我要告诉你一个坏消息。你不应该使用那个类作为基类,因为它有太多的限制和陷阱。虽然可能会有所帮助,但为什么要冒险呢?向库供应商提交错误报告。

如果你不需要多态指针,在你的类中包含一个该类型的对象,并委托你想要继承的成员函数即可。

class my_class {
private:
    evil_class evil;
public:
    virtual ~my_class() {/* stuff */}
    virtual int member() { return evil.member(); }
};

正确的做法是这样的,这应该被标记为答案。 - spt025

12

这是第三方SDK中的一个bug。任何用作基类的类都应该有虚析构函数。否则,当你删除指向派生类实例的基类指针时,派生类的析构函数将不会被调用。

绕过此问题的一种方法是不要删除指向基类的指针。相反,使用dynamic_cast获取指向派生类的指针(如果有许多从该基类派生的类,则可能会不方便)。


9
在这种情况下,您需要为您的类添加虚拟析构函数,而不是从此处删除编译标志。
例如,对于类Myclass,如果出现此错误,则需要添加。
virtual ~Myclass(){}

尝试这个解决方案,它会很好地工作。


4
当基类拥有虚函数但没有虚析构函数时会产生此警告。这是一个错误。如果您没有代码,那么除了确保手动释放子类中的任何资源之外,您无法采取其他措施。例如,在自定义cleanup()成员函数中,您需要确保在删除对象之前手动调用该函数。

另一个选择是将其static_cast到正确的类。请注意,不需要使用dynamic_cast(它会产生运行时开销并需要RTTI)。编译器可以在这种情况下很好地在编译时推导出类型关系。
当然,如果对象在您的代码之外的某个地方被删除,则您就没有办法了。在这种情况下,请确保您的子类不分配任何内容。这样,即使未调用析构函数,也不可能发生泄漏。

1
无论基类型是否具有虚函数,如果基类型没有虚析构函数,则通过指向基类型的指针删除派生类型的对象会产生未定义的行为。当然,没有虚函数意味着不太可能有人会玩弄指向基类型的指针。 - Pete Becker
3
当没有虚函数存在时,GCC不会发出警告。问题仍然存在,但没有警告。在2011标准之前的C++中没有final关键字,因此假定没有虚函数的类不应该被继承。如果是这种情况,那么这将是OP的错误。但在这种情况下,是SDK的错误。只是想指出这一点。 - Nikos C.
“假设”隐藏了很多含糊不清的东西。C++标准中有几个类没有虚函数,但是打算用作基类。例如,模板类“iterator”为其派生类型提供了一些typedefs。只要您不尝试通过指向迭代器的指针删除派生类型的对象,这就不是问题。一些警告是关于编译器作者的概念,即我不够聪明,无法使用明确定义且明确有意义的语言功能。 - Pete Becker
我没有从所讨论的类中创建任何子类。 - bentaisan

3

如果基类没有声明虚析构函数,你就不能安全地使用指向基类的指针。由于这是一个供应商库,你无法添加所需的虚析构函数。

如果库本身没有创建此对象的子类,您可以声明此对象的子类并将其用作基类来实现所需效果。

class NotGoodBase {
 ~NotGoodBase(); // Non-virtual destructor.  Possibly added by compiler.
};

class UseThisAsBase : public NotGoodBase {
 virtual ~UseThisAsBase(); // Virtual destructor.
};

您应该能够在任何可以使用NotGoodBase的地方使用UseThisAsBase类型的指针,除了需要具有NotGoodBase类型的LValue的地方,例如赋值或通过引用传递变量时。


2
实际上,他也许能够在头文件中添加虚析构函数。Hack,hack,kluge。这取决于是否在头文件中声明但没有定义。 - Jive Dadson

-4

实际上,我从编译中删除了-Werror开关,程序就编译成功了。

现在,这些信息只是警告。

我将向供应商发送一个错误报告。


3
不是我认为正确的方式,除非没有其他方法,否则您不应更改编译标志以使您的代码编译。 - spt025

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接