【问题标题】:How to delete an object of a polymorphic class type that has no virtual destructor如何删除没有虚拟析构函数的多态类类型的对象
【发布时间】:2012-10-11 06:55:00
【问题描述】:

当我尝试从第三方 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 或库对象中完成的。

我的构建环境是带有 gpp 的 Eclipse Juno。

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

那么,如果我无法修改供应商代码的黑匣子部分,我有哪些选择?

这是在制作过程中失败的代码:

delete pData->unit;

【问题讨论】:

  • 所以这个类是用来继承的,但没有虚拟析构函数?我会向制作那个图书馆的人抱怨。
  • 警告有点温和。通过指向没有虚拟析构函数的基类型的指针删除派生类型的对象会导致未定义的行为。没有“可能”。
  • @PeteBecker 至少 g++ 4.8 会发出此警告,即使我通过指向派生类的指针删除。这将是安全的(AFAIK),因此“可能”在这里也是错误的(根本不应该生成警告)。我猜g++无法区分这两种情况。
  • @PeterBecker 在我的情况下,它当然可以是进一步的派生类(在这种情况下警告是正确的),但这种情况(恕我直言)永远不能被排除。

标签: c++ inheritance destructor


【解决方案1】:

恐怕是个坏消息。您不应将该类用作基类。太多的限制和陷阱。你可能会侥幸逃脱,但为什么要冒险呢?向库供应商提交错误报告。

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

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

【讨论】:

  • 正确的做法,这应该被标记为答案
【解决方案2】:

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

解决它的一种方法是不删除指向基址的指针。相反,使用 dynamic_cast 来获取派生类的指针(如果从该基类派生的类很多,这可能会很不方便)。

【讨论】:

    【解决方案3】:

    当基类具有虚拟成员函数但没有虚拟 dtor 时会产生此警告。这是一个错误。如果您没有代码,那么除了确保您手动取消分配子类中的任何资源外,您无能为力。就像在自定义 cleanup() 成员函数中一样,您确保在删除对象之前手动调用它。

    另一种选择是将static_cast 分配给正确的类。请注意,不需要dynamic_cast(会产生运行时开销并需要 RTTI)。在这种情况下,编译器可以在编译时很好地推导出类型关系。

    当然,如果对象在其他不属于您的代码的地方被删除,那么您就不走运了。在这种情况下,请确保您的子类不分配任何东西。这样即使没有调用析构函数也不会泄漏。

    【讨论】:

    • 基类型是否有虚函数无关紧要;如果基类型没有虚拟析构函数,则通过指向基的指针删除派生类型的对象具有未定义的行为。当然,没有虚函数意味着不太可能有人会摆弄指向基类型的指针。
    • @PeteBecker GCC 在没有虚函数时不会发出警告。问题仍然存在,但没有警告。 2011 标准之前的 C++ 没有 final 关键字,因此假定不应该继承没有虚函数的类。如果是这样,那将是 OP 的错误。在这种情况下,这是 SDK 的错误。只是想指出这一点。
    • “假设”隐藏了大量的模糊性。 C++ 标准中有几个类没有虚函数,但旨在用作基类。例如,模板类iterator 为其派生类型提供了一些类型定义。只要您不尝试通过指向迭代器的指针删除派生类型的对象,这不是问题。一些警告是关于编译器作者的想法,即我不够聪明,无法使用定义明确且意义明确的语言功能。
    • 我没有从相关类创建任何子类。
    【解决方案4】:

    在这种情况下,您需要将虚拟析构函数添加到您的类中,而不是从此处删除编译标志。

    例如。对于Myclass 类,这个错误来了,然后添加

    virtual ~Myclass(){}
    

    试试这个灵魂,它会很好用。

    【讨论】:

      【解决方案5】:

      您不能安全地使用指向基类的指针,除非它在基类上声明了虚拟析构函数。由于这是一个供应商库,您无法添加所需的虚拟析构函数。

      如果库本身不创建该对象的子类,您可以通过为该对象声明一个子类并将该对象用作基类来获得所需的效果。

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

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

      【讨论】:

      • 其实他或许可以在头文件中添加虚拟析构函数。哈克,哈克,克卢格。取决于是否在此处声明但未在此处定义。
      【解决方案6】:

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

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

      我会向供应商发送错误报告。

      【讨论】:

      • 在我看来不是正确的方法,你不应该改变你的编译标志,以便你的代码编译,除非没有其他方法
      猜你喜欢
      • 2017-01-30
      • 2015-06-04
      • 2016-06-14
      • 2014-10-29
      • 2020-11-26
      • 2020-10-29
      • 2014-02-23
      • 1970-01-01
      • 2011-03-16
      相关资源
      最近更新 更多