使用相同的变量销毁然后构造新对象

时间:2012-01-12 02:56:54

标签: c++ placement-new

有时重新开始很好。在C ++中,我可以使用以下简单的操作:

{

    T x(31, Blue, false);

    x.~T();                        // enough with the old x

    ::new (&x) T(22, Brown, true); // in with the new!

    // ...
}

在范围的最后,析构函数将再次运行,一切似乎都很好。 (我们也说T有点特别,不喜欢被分配,更不用说交换了。)但有些东西告诉我,摧毁一切并再试一次并不总是没有风险。这种方法有可能存在吗?

6 个答案:

答案 0 :(得分:28)

我认为使其真正安全使用的唯一方法是要求被调用的构造函数为noexcept,例如添加static_assert

static_assert(noexcept(T(22, Brown, true)), "The constructor must be noexcept for inplace reconstruction");
T x(31, Blue, false);
x.~T();
::new (&x) T(22, Brown, true);

当然这只适用于C ++ 11。

答案 1 :(得分:17)

如果T的构造函数抛出第二个构造,则会出现问题。如果你喜欢蛮力方法,请检查:

T x(31, Blue, false);
x.~T();
const volatile bool _ = true;
for(;_;){
  try{
    ::new (&x) T(22, Brown, true);
    break; // finally!
  }catch(...){
    continue; // until it works, dammit!
  }
}

它甚至提供强大的异常保证!


更严重的是,它就像踩着地雷一样,知道如果你移动你的脚就会熄灭它......

实际上解决双重破坏的未定义行为的方法:

#include <cstdlib>

T x(31, Blue, false);
x.~T();
try{
  ::new (&x) T(22, Brown, true);
}catch(...){
  std::exit(1); // doesn't call destructors of automatic objects
}

答案 2 :(得分:9)

如果T的构造表达式抛出,则会双重破坏对象,即UB。当然,即使是这样做的愿望也表明设计失败了。

答案 3 :(得分:7)

我试着编译它,但我只敢在调试器下运行它。所以我看看我的旧编译器生成的反汇编(注释也是编译器):

@1 sub nerve.cells, fa0h
@2 xor x, x     // bitch.
@3 mov out, x
@4 test out, out
@5 jne @1
@6 xor x, x     // just in case.
@7 sub money, 2BC   // dammit.
@8 mov %x, new.one
@8 cmp new.one, %x 
@9 jne @7   
...
@25 jmp @1      // sigh... 

答案 4 :(得分:2)

嗯。既然你正在做C ++不鼓励的事情,我想每个人都忘记了转到

请注意,在显式X.~T()调用之后,在重构 1 之前,如果有人在声明/初始化之前执行了goto,则仍会有双重破坏变量x(甚至在内部范围块中)。

因为你显然可以记录下来,所以我不会轻易尝试“修复”这个问题。从概念上讲,您可以设计一个RAII类来管理对象的现场重建,使得这种机动在任何地方都可以安全地使用goto。请注意,您可以从RAII管理器对象的析构函数中完美地转发placement-new构造函数调用。生活很美好。

其他警告当然仍然适用(见其他答案)


1 我们可以假设暂时没有建造

答案 5 :(得分:0)

没有什么可以阻止你这样做,它在大多数情况下都会起作用。但正如许多C ++中的情况一样,知道你的案例的具体细节将是你想要的工作和核心转储之间的区别。我可以看到为什么你想在真正的程序中这样做的原因很少,唯一有意义的是内存映射文件。