c++中placement new怎么用_c++原地构造函数【深度】

8次阅读

placement new 是在已分配内存上显式调用构造函数的 new 重载形式,不分配内存、不管理生命周期;必须手动对齐、构造、析构,且不能用 delete 释放。

c++中placement new怎么用_c++原地构造函数【深度】

placement new 是什么,不是什么

placement new 不是“原地构造函数”,它只是 new 的一个重载形式,用于在**已分配的内存地址上显式调用构造函数**。它不分配内存,也不管理生命周期——这两件事必须由你手动负责。常见误解是把它当成“替代 new 的安全方式”或“自动内存复用工具”,其实它更像一把裸露的扳手:能拧紧螺丝,但拧错位置会崩牙。

怎么写 placement new 调用,参数和类型必须严格匹配

标准写法是:new (ptr) T(args...),其中 ptr 是类型为 T* 或可隐式转为 T*指针(注意不是 void* 直接传),T 必须有可访问的构造函数。编译器不会帮你做对齐检查或大小验证。

常见错误:

  • char buf[100]; new (buf) std::String("hello"); —— 错!bufchar*,而 std::string 构造需要 std::string* 语义地址,虽能编译但触发未定义行为(UB)
  • 正确写法:new (static_cast(buf)) std::string("hello"); 或更安全地用 std::align 对齐后传 void*
  • T 是带对齐要求的类型(如 std::max_align_t 或 AVX 类型),而 buf 未按需对齐,placement new 后访问对象直接 UB

析构必须手动调用,且顺序不能反

用 placement new 构造的对象,delete 无效,也不会自动调用析构函数。你必须显式写 obj.~T()。漏掉这步,资源泄漏(如文件句柄、内存)几乎必然发生;调早了(比如在对象还在用时就析构),也是 UB。

立即学习C++免费学习笔记(深入)”;

典型场景(如对象池):

char pool[4096]; auto* p = new (pool) MyType(123); // 构造 // ... 使用 p ... p->~MyType(); // 必须显式析构,不能省 // 此时 pool 内存仍可用,但对象已销毁

注意:p->~MyType()(*p).~MyType() 等价,但不能写成 delete p —— 它没走 operator delete,会 crash 或静默损坏堆。

和 operator new(size_t, void*) 的关系别搞混

placement new 能工作,是因为标准库提供了这个全局重载:void* operator new(std::size_t, void* p) noexcept,它只返回 p,不做任何事。你不能删掉它,也不能自己重定义它(否则违反 ODR)。如果自定义了类专属的 operator new(size_t, void*),那它会被优先调用——但绝大多数情况不需要,也容易引入二义性。

真正要小心的是:如果你在模板里泛化使用 placement new,而 T 重载了带 void* 参数的 operator new,行为可能偏离预期。稳妥做法是强制走全局版本:::new (ptr) T(...)

对齐、异常、noexcept 都得自己兜底——标准 placement new 不抛异常,但 T 的构造函数可能抛。没捕获的话,对象处于“半构造”状态,析构函数不会被调用,极易泄漏。

text=ZqhQzanResources