【问题标题】:Manually calling destructor before delete删除前手动调用析构函数
【发布时间】:2017-08-28 16:35:10
【问题描述】:
auto obj = new Object;
obj->~Object();
delete obj;

我知道这很不寻常,但这是定义的行为吗?它会导致任何令人惊讶的问题吗?

【问题讨论】:

  • 如果从 WhiZTiM 的回答中不完全清楚,它未定义的原因是因为它会为同一对象连续调用 两个 析构函数。

标签: c++ destructor undefined-behavior delete-operator


【解决方案1】:

您只能这样做如果obj指向的已破坏对象替换为新对象:

auto obj = new Object;
obj->~Object();

new (obj) Object();
delete obj;

否则,您将调用未定义的行为。


你应该明白:

  • new调用operator new获取内存,然后调用提供的构造器创建对象
  • delete 调用对象的析构函数,然后 调用operator delete 以“返回”内存。


编辑:正如 Bo Persson 指出的那样,如果您不能提供异常保证,这不是一个好主意

【讨论】:

  • 如果您成功替换该对象。如果构造函数抛出,你仍然是吐司。
  • 如果一个对象的销毁必须在内存被释放之前发生,有没有办法告诉C++“这个指针标识一个已被销毁但尚未被销毁的对象使用的分配存储已删除;请在不调用析构函数的情况下释放存储”?
  • @supercat,是的......但它需要更多的工作。虽然您可以在指针上显式调用operator delete(obj),但在存在class-specific operator delete 的情况下它会失败。当delete 表达式 调用operator delete (...) 时,标准要求它首先检查类特定 重载。但是当你在代码中明确调用operator delete 时,这个doesn't happen.
  • @supercat, This 是检查特定类重载的常规情况。 Double destructor hence UB 是 OP 的情况。 This 是您自己显式调用它时发生的情况,与 regular 情况相反。正确的方法是this,但是,如果您不知道是否存在 class-specific 重载,该怎么办?更复杂?
【解决方案2】:

导致对象析构函数被调用两次是未定义的行为。您没有遵守规则,并且允许编译器对您的代码做任何事情。只是不要那样做

【讨论】:

    【解决方案3】:

    如果我们严格遵循标准语言,您的代码会导致未定义的行为,并会导致问题。

    但是,如果任何平台遇到以下问题,我会感到非常惊讶:

    struct Foo {};
    
    void testFoo()
    {
       Foo* foo = new Foo();
       foo->~Foo();
       foo->~Foo();
       foo->~Foo();
       foo->~Foo();
       foo->~Foo();
       foo->~Foo();
       foo->~Foo();
       delete foo;
    }
    

    另一方面,如果任何平台都可以执行以下操作而不会遇到问题,我会感到非常惊讶。

    struct Bar {std::string s;};
    
    void testBar()
    {
       Bar* bar = new Bar{"test"};
       bar->~Bar();
       delete bar;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-04-24
      • 2012-03-13
      • 2014-01-15
      • 2020-04-01
      • 2014-12-27
      • 2013-09-30
      • 2013-11-16
      • 1970-01-01
      相关资源
      最近更新 更多