【问题标题】:How to delete an object of derived class that has no dstructor如何删除没有 dstructor 的派生类的对象
【发布时间】:2023-03-19 07:10:01
【问题描述】:

鉴于这个程序:

struct Base
{
  virtual void f() {}
};

struct Derived:public Base
{
};

int main()
{
  Derived* c = new Derived;
  delete c;
}

gcc-4.4 -Wall 很好,但 gcc-5.2 -Wall 给出警告:删除具有非虚拟析构函数的多态类类型“Derived”的对象可能会导致未定义的行为 [-Wdelete-non-virtual-dtor]

我看到了the discussion on deleting a base pointer,但在我的情况下,它是派生对象。我认为这是一个 gcc 错误,但显然是GNU doesn't think so。有没有办法在不改变基类定义的情况下摆脱警告?

【问题讨论】:

  • 代码是正确的;你可以忽略警告。

标签: c++ compiler-warnings derived-class


【解决方案1】:

GCC 完全有权发出该警告。为什么?因为除非你声明Derivedfinal,否则某人完全有可能创建一个派生自DerivedMoreDerived 类型。此时,您删除 Derived 指针可能非常无效。

virtual 析构函数添加到Base 并没有真正的缺点。是的,析构函数将是一个虚拟调用。但这几乎不会成为性能瓶颈。

【讨论】:

  • 在派生类中添加“final”可以解决问题。我无法更改供应商提供的 Base 类。好吧,我想我可以更改 .h 但不能更改 .so。几个离题但相关的问题: 1. 如果没有 ~Base(),我可以安全地添加“virtual ~Base() {}”吗? 2.如果Base中有非virtual dtor,我可以安全添加virtual吗?
  • 你的供应商真的应该解决这个问题,这是 C++ 中的一个大禁忌,也是一个常见的求职面试问题,
  • 不,警告是错误的。指针的类型是Derived,不管是否有MoreDerived类型,在显示的上下文中删除都没有问题。当然,在不同的情况下,警告是正确的,但这里不是这样。
  • @PeteBecker:编译器怎么知道呢?为什么编译器要费心检查像这样过于简化的情况,即对象在被销毁之前就被分配了?
  • @PeteBecker:编译器对一直“很好”的代码发出警告。这就是 for 的警告:警告您,虽然您编写的代码是合法的 C++,但它可能没有按照您的意愿行事。这与if(x = 4) 上的警告没有什么不同。嗯,不同之处在于在这种情况下关闭编译器有一个通用的约定。
【解决方案2】:

virtual ~Base() {} 添加到 Base 将修复警告。

因此将 final 添加到 Derived。

class Derived final : public Base
{
    ...
};

向 Derived 添加虚拟析构函数也是如此。

class Derived : public Base
{
public:
    virtual ~Derived() {}

    ...
};

另外,如果您不打算多态地使用 Derived,您可以私下继承它。这不会阻止警告,但您永远不能将 Derived 指针分配给 Base 指针,因此永远不能通过 Base 指针删除 Derived。

class Derived : private Base
{
    ...
};

【讨论】:

  • 我知道代码是 100% 合法的,按预期工作,并且没有未定义的行为。同时,有时人们希望摆脱代码中的所有警告。由于 GCC 可能不会更改警告,因此需要一些其他解决方案。
  • 是的,对不起。另一个答案的坚持。您的只是对问题的直接回答,解释了如何摆脱警告。我删除了我的评论。
【解决方案3】:

C++ 标准 [expr.delete] 第 3 段 [ISO/IEC 14882-2014] 声明如下:

在第一种选择(删除对象)中,如果要删除的对象的静态类型与其动态类型不同,则静态类型应为要删除的对象的动态类型的基类和静态类型的基类。类型应具有虚拟析构函数或行为未定义。在第二种选择(删除数组)中,如果要删除的对象的动态类型与其静态类型不同,则行为未定义。

不要通过指向具有非虚拟析构函数的基类类型的指针来删除派生类类型的对象。相反,应使用虚拟析构函数定义基类。通过指向没有虚拟析构函数的类型的指针删除对象会导致未定义的行为。 在这个兼容的解决方案中,Base 的析构函数具有显式声明的虚拟析构函数,确保多态删除操作产生明确定义的行为。

struct Base {
  virtual ~Base() = default;
  virtual void f();
};

struct Derived : Base {};

void f() {
  Base *b = new Derived();
  // ...
  delete b;
}

如果使用此链接,您可以找到更多信息:https://wiki.sei.cmu.edu/confluence/display/cplusplus/OOP52-CPP.+Do+not+delete+a+polymorphic+object+without+a+virtual+destructor

【讨论】:

    猜你喜欢
    • 2020-03-12
    • 1970-01-01
    • 2012-12-30
    • 1970-01-01
    • 1970-01-01
    • 2012-02-05
    • 1970-01-01
    • 2015-04-26
    • 2011-07-20
    相关资源
    最近更新 更多