销毁,然后使用相同的variables构造新的对象

有时从头开始很好。 在C ++中,我可以使用下面这个简单的操作:

{ T x(31, Blue, false); x.~T(); // enough with the old x ::new (&x) T(22, Brown, true); // in with the new! // ... } 

在范围的末尾,析构函数会再次运行,一切都很顺利。 (让我们也说T有点特别,不喜欢被分配,更不用说交换了。)但是,有些东西告诉我,并不总是没有风险去摧毁一切,然后再试一次。 这种方法有没有可能发生?

我认为唯一可以安全使用的方法是要求被调用的构造函数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。

如果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 } 

如果T的构造expression式抛出,则会双击破坏对象,即UB。 当然,即使这样做的愿望是一个devise失败的指示。

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

 @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... 

嗯。 既然你正在做的一切,C ++阻止,我想每个人都忘记了goto

请注意,在显式X.~T()调用之后,在重构1之前,如果有人在variablesx的声明/初始化之前( 甚至在内部作用域块内 )做了goto ,仍然会有双重破坏。

既然你可以明显地logging下来,那么我就不会经历这个“修复”这个麻烦了。 你可以在概念上devise一个RAII类来pipe理对象的重build,使得这个操作在任何地方都是安全的。 请注意,您可以通过RAIIpipe理器对象的析构函数将位置新的构造函数调用完美转发。 生活很好。

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


1我们可以假设这个时刻不是build设性的

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