在成员函数中调用析构函数后用 placement new 重构是否为定义行为?
手动调用析构后用placement new重构对象是否属于定义行为?
先看你给出的代码:
#include <new> struct thing { thing() = default; void foo() { this->~thing(); new (this) thing(); } };
咱先把结论撂这儿:在这个特定场景下,你的代码完全符合C++标准的定义行为,但得掰扯清楚几个关键前提和容易踩坑的细节,不然换个场景就可能踩进未定义行为(UB)的坑里。
为什么你的代码是合法的?
核心原因在于你的thing类是平凡可析构且平凡可构造的类型:
- 你用
=default声明了默认构造函数,没有自定义析构、拷贝/移动构造或赋值操作,编译器会生成完全“平凡”的这些函数——简单说就是这些函数不会做任何额外操作(比如释放动态内存、关闭句柄等)。 - 手动调用
this->~thing():这个操作只会销毁当前对象的状态,但不会释放this指向的内存(因为内存是对象原本占用的栈/堆空间,生命周期还没结束)。 - 紧接着用
new (this) thing()做placement new:这一步会在this指向的有效内存位置,重新构造一个thing对象。因为这块内存本来就是为thing类型分配的,对齐要求完全满足,而且内存没有被释放,所以是合法的。
哪些情况会变成未定义行为?
别以为这种写法万能,换个场景就可能翻车:
- 如果
thing有非平凡析构函数:比如后来加了std::string成员,或者自定义了析构函数来释放动态内存。这时候手动调用析构后,必须确保placement new之前没有访问已销毁的成员,而且placement new能正确初始化新对象(比如你的例子里如果是std::string,析构后内存被释放,placement new会重新构造空字符串,这时候是没问题的,但如果中间有其他操作碰了原来的成员,就炸了)。 - 如果
this指向的内存已经失效:比如对象是堆上的,已经被delete了,再调用foo(不过你的例子里foo是成员函数,调用时this肯定指向存活的对象,所以不存在这个问题)。 - 如果placement new的内存对齐不符合要求:但
this本来就是thing类型的指针,对齐肯定满足,所以这个坑你没踩到。
额外注意事项
虽然合法,但这种写法在工程里要谨慎用:
- 后续如果有人修改
thing类,添加了非平凡成员或函数,很容易不小心引入UB,而且这种问题很难排查。 - 调用
foo之后,原来的对象已经被销毁,新对象在同一内存位置构造,this指针依然指向这个新对象,后续对this的操作都是合法的——因为内存地址没变,类型也完全一致。
内容的提问来源于stack exchange,提问作者DarthRubik
相关产品推荐
相关产品推荐

