首页 > 解决方案 > 在堆栈上使用带有变量的新位置是否正确?

问题描述

让我们看一下这段代码:

A a(123);
new(&a) A(124);

测试表明,在这种情况下,当程序关闭时,析构函数~A()将被调用一次。因此,如果A我们有一些指针作为字段,我们将得到内存泄漏。

A a(123);
a.~A();
new(&a) A(124); 

在这里一切都会是正确的。但是根据标准在析构函数调用后使用对象是未定义的行为(尽管大多数编译器提供的行为没有一些麻烦)。

我可以获取已调用析构函数的对象的地址吗?在堆栈变量上调用放置新是正确的操作吗?

标签: c++new-operatorplacement-newstack-pointer

解决方案


我可以获取已调用析构函数的对象的地址吗?

[编辑:] 这样的地址是有效的,所以如果你有一个指向它的指针,它是有效的。
事后你能知道它的地址吗,我不太确定。

basic.life.6 […] 在对象的生命周期结束之后,在对象占用的存储空间被重用或释放之前,任何表示对象所在存储位置地址的指针都可以使用,但只能在有限的方式。[…] 这样的指针指的是分配的存储空间,并且像指针是 void* 类型一样使用指针是明确定义的。

查看全文以了解所有限制,但允许在placement-new 中使用。


至于评论,我认为您的两个样本在您展示的范围内都是正确的。

A a(123);
new(&a) A(124);

鉴于我们在样本中所知道的,我认为这是正确的。

根据basic.life.5,通过重新使用对象的存储来结束对象的生命周期是有效的。唯一的条件是程序不依赖于析构函数产生的副作用——为了安全起见,我只会在可简单破坏的类型上这样做。否则,您需要一个显式的析构函数调用,就像您在那里所做的那样:

A a(123);
a.~A();
new(&a) A(124); 

我没有看到任何阻止这种情况的规则。该标准甚至明确提到了这种结构何时无效:

如果一个程序:

  • 以静态、线程或自动存储持续时间结束 T 类型对象的生命周期
  • 当隐式析构函数调用发生时,另一个原始类型的对象不占用相同的存储位置,

程序的行为是未定义的。

(格式化为我的项目符号)

虽然不是一个明确的证据,但这段话表明在其他情况下,行为是有定义的。我想不出他会违反的另一条规则。

(请注意我使用标准的 C++20 版本)


推荐阅读