You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++20/23标准:不调用析构函数用placement new复用存储是否合规?

C20/C23中placement new复用存储的合法性分析

一、通用场景:未调用析构函数即复用存储的行为定义

在C20和C23标准的**[basic.life](对象生命周期)**章节中,明确规定了对象存储复用的约束:

  • 当对象的存储被重用(如通过placement new在同一块存储创建新对象)时,原对象的生命周期会结束。但如果原对象拥有非平凡析构函数,则必须在存储被重用前显式调用原对象的析构函数,否则行为属于未定义(UB)。
  • 对应标准条款:C20 [basic.life]/11、C23 [basic.life]/12(条款编号略有调整,但核心规则一致),具体表述为:若原对象析构函数非平凡,且新创建的对象与原对象类型(忽略cv限定)相同,则必须先调用原对象的析构函数,才能在同一块存储创建新对象,否则行为未定义。

针对你给出的示例代码:

alignas(T) std::byte storage[sizeof(T)];
T* p = new (storage) T(...);
p = new (storage) T(...); // 未调用原对象析构
p->~T();

如果T的析构函数是非平凡的(比如包含自定义析构逻辑、成员对象有非平凡析构等),这段代码属于未定义行为——原对象的析构函数未被调用,其持有的资源可能无法正确释放,甚至导致内存泄漏、对象状态混乱等问题。

二、合法复用的特殊情形:平凡析构函数

当T具有平凡析构函数时,上述复用行为是合法的,具体依据标准**[basic.life]/12**(C20)、[basic.life]/13(C23):

  • 平凡析构函数指不需要执行任何自定义逻辑的析构函数(比如编译器自动生成的、无虚函数、成员和基类都没有非平凡析构的析构函数)。
  • 对于拥有平凡析构函数的对象,其生命周期结束时不需要显式调用析构函数。此时直接通过placement new在同一块存储创建同类型新对象,原对象的生命周期会自动结束,不会触发未定义行为。

回到示例代码:如果T是平凡析构类型,那么第二次调用placement new时,原对象的生命周期正常结束,无需显式调用其析构函数;最后对新对象调用析构函数(即使是平凡的)也是合法的,不会产生问题。

内容的提问来源于stack exchange,提问作者jacobsa

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 11:36:06