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

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");—— 错!buf是char*,而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 的构造函数可能抛。没捕获的话,对象处于“半构造”状态,析构函数不会被调用,极易泄漏。











