有时重新开始很好。在C ++中,我可以使用以下简单的操作:
{
T x(31, Blue, false);
x.~T(); // enough with the old x
::new (&x) T(22, Brown, true); // in with the new!
// ...
}
在范围的最后,析构函数将再次运行,一切似乎都很好。 (我们也说T
有点特别,不喜欢被分配,更不用说交换了。)但有些东西告诉我,摧毁一切并再试一次并不总是没有风险。这种方法有可能存在吗?
答案 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 ++中的情况一样,知道你的案例的具体细节将是你想要的工作和核心转储之间的区别。我可以看到为什么你想在真正的程序中这样做的原因很少,唯一有意义的是内存映射文件。