手动调用析构函数在删除之前

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

我知道这不太寻常,但它是否是定义行为?它会导致任何意外的问题吗?


如果从WhiZTiM的回答中还不是完全明显,那么它未定义的原因是因为会连续调用两次相同对象的析构函数。 - HolyBlackCat
3个回答

8

只有在您用以下方式替换obj指向的被破坏的对象时,才能这样做:如果

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

new (obj) Object();
delete obj;

否则,您将调用未定义的行为。
您应该了解以下内容:
- new 调用 operator new 来获取内存,然后调用提供的构造函数来创建对象。 - delete 调用对象的析构函数,然后调用 operator delete 来“返回”内存。
编辑:正如 Bo Persson 指出的那样,如果您无法提供异常保证,这并不是一个好主意。

3
如果你成功地替换了对象,但构造函数抛出异常,你仍然会失败。 - Bo Persson
如果需要在内存被释放之前销毁对象,是否有办法告诉C++“此指针标识已销毁但尚未删除的对象使用的已分配存储;请释放存储而不调用析构函数”? - supercat
@supercat,是的...但这需要更多的工作。虽然您可以在指针上明确调用 operator delete(obj),但在存在类特定的 operator delete的情况下,它将失败。当delete 表达式调用operator delete (...)时,标准要求首先检查类特定重载。但是当您在代码中显式调用operator delete时,这种情况不会发生 - WhiZTiM
@supercat,这个是检查类特定重载的常规情况。双重析构因此UB是OP案例中发生的事情。这个是当你自己显式调用它时发生的情况,与常规情况相反。正确的方法应该是这个,但是,如果您不知道是否有类特定重载呢?更复杂吗? - WhiZTiM

1

如果导致一个对象的析构函数被调用两次,那么这是未定义行为。您没有遵循规则,编译器可以随意处理您的代码。只要不要这样做


0

如果我们严格按照标准语言来看,你的代码会导致未定义行为并引起问题。

然而,如果使用以下代码,我会非常惊讶如果任何平台遇到任何问题:

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;
}

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