内存重用和UB

时间:2014-10-22 05:35:42

标签: c++

以下代码是否产生UB?

#include <iostream>
#include <climits>

struct A
{
    ~A(){ std::cout << "Non-trivial" << std::endl; }
};

int main()
{
    A a;
    new (&a) A; 
}   //UB?

DEMO

标准N3797::3.8/8 [basic.life]

  

如果程序以静态结束T类型对象的生命周期   (3.7.1),线程(3.7.2)或自动(3.7.3)存储持续时间和if   T有一个非平凡的析构函数,程序必须确保一个   原始类型的对象占用相同的存储位置时   隐式析构函数调用发生;否则的行为   该程序未定义。

我认为主函数离开时没有UB,因为&a指向与最初分配的类型相同的类型。

2 个答案:

答案 0 :(得分:1)

  

[3.8 / 4]程序可以通过重用对象占用的存储或通过使用非平凡的析构函数显式调用类类型的对象的析构函数来结束任何对象的生命周期。对于具有非平凡析构函数的类类型的对象,程序不需要在重用或释放对象占用的存储之前显式调用析构函数;但是,如果没有对析构函数的显式调用,或者如果没有使用 delete-expression (5.3.5)来释放存储,则不应该隐式调用析构函数并且 任何依赖于析构函数产生的副作用的程序都有未定义的行为

您的程序“依赖”析构函数产生的副作用吗?清楚:

  • 可观察程序输出取决于 - 在变化的意义上 - 是否运行析构函数

  • 如果析构函数未运行,cout缓冲区,位置等的状态将不同,

  • 在程序终止时留下的刷新操作可能会有所不同。

你可以合理地说,后来的程序行为/流程取决于析构函数是否运行,这意味着技术上未定义的行为。

尽管如此,我仍然担心标准的意图是警告析构函数操作如不释放锁定或减少引用计数器可能会产生令人不快的后果,例如后来的挂起或泄漏,以及实际上在所有实际编译器中你发布的程序会表现得像你期望的那样。

另外值得注意的是,一般情况下,如果构造函数抛出并且在该内存位置没有有效对象的情况下展开范围,则实践会很危险。

答案 1 :(得分:0)

这是未定义的行为,但不是因为你引用的原因:

  

[basic.life] / 4

     

[...]如果没有显式调用析构函数或   如果没有使用delete-expression(5.3.5)来释放存储,那么   不应该隐式调用析构函数和任何依赖的程序   关于析构函数产生的副作用有不明确的行为。

由于你的析构函数有副作用,你必须在重用内存之前显式调用析构函数

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