【发布时间】:2019-02-08 17:49:00
【问题描述】:
我试图弄清楚以下是否是未定义的行为。我感觉它不是 UB,但我对标准的阅读使它看起来像是 UB:
#include <iostream>
struct A {
A() { std::cout << "1"; }
~A() { std::cout << "2"; }
};
int main() {
A a;
new (&a) A;
}
引用 C++11 标准:
basic.life¶4 说“程序可以通过重用对象占用的存储空间来结束任何对象的生命周期”
所以在new (&a) A 之后,原来的A 对象已经结束了它的生命周期。
class.dtor¶11.3 表示“当创建对象的块退出 ([stmt.dcl]) 时,对具有自动存储持续时间 ([basic.stc.auto]) 的构造对象隐式调用析构函数”
所以当main 退出时,原始A 对象的析构函数被隐式调用。
class.dtor¶15 表示“如果为生命周期已结束 ([basic.life]) 的对象调用析构函数,则行为未定义。”
所以这是未定义的行为,因为原来的 A 不再存在(即使新的 a 现在存在于同一个存储中)。
问题是是否调用了原始A 的析构函数,或者是否调用了当前名为a 的对象的析构函数。
我知道basic.life¶7,它说名称a 指的是放置new 之后的新对象。但是 class.dtor¶11.3 明确表示它是 退出范围的对象的析构函数 被调用,而不是由退出范围的名称引用的 对象的析构函数 .
我是误读了标准,还是这实际上是未定义的行为?
编辑:有几个人告诉我不要这样做。澄清一下,我绝对不打算在生产代码中这样做!这是针对CppQuiz 的问题,这是关于极端案例而不是最佳实践的问题。
【问题讨论】:
-
找到另一个同意的答案:stackoverflow.com/a/35395517/3980929(可能不那么受骗,但仍然很有趣)
-
您不应在现有对象上使用新放置,因为先前构造的对象不会被破坏。如果通过运行上述程序,输出是 '112' 或
1122,那么它是未定义的行为。您只需要一些想象力,例如如果 A 中有一个指针(可能是间接的)会发生什么,并且您可以猜测它是未定义的行为。 -
@Phil1970 [basic.life]p5 would like to have a word with you。此外,它说如果输出为 1122,则编译器有错误,因为标准明确禁止这样做!
-
@Rakete1111 我在那个问题中看不到任何与这个问题相关的内容,除了这是一个非常广泛的问题,它询问了关于放置新位置的所有内容else - 关于那个仅凭我认为这是一个糟糕的欺骗目标 tbh
-
标准要求它以这种方式发生,但我同意可以更好地解释它。 Peter's comment 中的规则必须存在的原因是因为否则会混淆“被覆盖对象的现有指针/引用/变量名现在引用新对象”是否包含析构函数调用。
标签: c++ language-lawyer undefined-behavior object-lifetime placement-new