是`new(this)MyClass();`直接调用析构函数后的未定义行为?

Xeo*_*Xeo 9 c++ this placement-new undefined-behavior

在我的这个问题中,@ DeadMG说通过this指针重新初始化类是未定义的行为.标准中有没有提到它?

例:

#include <iostream>

class X{
  int _i;
public:  
  X() : _i(0) { std::cout << "X()\n"; }
  X(int i) : _i(i) { std::cout << "X(int)\n"; }

  ~X(){ std::cout << "~X()\n"; }

  void foo(){
    this->~X();
    new (this) X(5);
  }

  void print_i(){
    std::cout << _i << "\n";
  }
};

int main(){
  X x;
  x.foo();
  // mock random stack noise
  int noise[20];
  x.print_i();
}
Run Code Online (Sandbox Code Playgroud)

Ideone的输出示例(我知道UB也可能是"看似正确的行为").
请注意,我没有在类之外调用析构函数,因为没有访问生命周期结束的对象.另请注意,@ DeadMG表示直接调用析构函数是可以的 - 因为它为每个构造函数调用一次.

sha*_*oth 8

如果它与堆栈展开没有冲突,那就没关系.

您销毁对象,然后通过指针重建它.如果您需要构造和销毁没有默认构造函数的对象数组,那么这就是您要做的.

问题是这是不安全的例外.如果调用构造函数抛出异常并且堆栈被解开并且第二次调用析构函数会怎么样?

{
   X x;
   x.foo(); // here ~X succeeds, then construction fails
} //then the destructor is invoked for the second time.
Run Code Online (Sandbox Code Playgroud)

那个方面特别是未定义的行为.

  • @Xeo:我不认为在一般情况下可以使异常安全.问题是在构造之前,你必须已经被破坏,如果构造函数失败,状态就不再是原始状态了.在一个带有nothrow移动构造函数的类中,你可以完全避免这个问题:构造一个局部变量,一旦构造就将它移动到`*this`.在这种情况下,它将是异常安全的(假设析构函数也不是). (3认同)