Placement New 是 operator new 的重载形式,仅调用构造函数而不分配内存;普通 new 则先分配内存再构造对象,二者本质区别在于是否解耦内存分配与对象构造。

placement new 是什么,和普通 new 有什么区别
placement new 不是独立的运算符,而是 operator new 的一个重载形式,它跳过内存分配阶段,只执行构造函数调用。普通 new 会先调用 operator new 分配内存,再在该地址上调用构造函数;而 placement new 直接在你提供的内存地址上构造对象——前提是这块内存已存在、足够大、且对齐正确。
常见错误现象:
- 调用后对象行为异常(如成员变量为垃圾值)→ 内存未初始化或对齐不足
- 程序崩溃在构造函数第一行 → 传入地址非法(比如 nullptr、只读页、栈溢出区域)
- 编译报错
no matching function for call to 'operator new'→ 忘了包含<new></new>
必须包含头文件:
#include <new>
怎么写 placement new 的基本调用
最简形式是:
T<em> p = new (buf) T(args...);
其中
buf 是 void 类型指针,指向已分配好的原始内存块。
关键点:
立即学习“C++免费学习笔记(深入)”;
-
buf必须满足T的对齐要求(可用alignof(T)检查) -
buf所指内存大小至少为sizeof(T),若T有虚函数或继承关系,仍只需sizeof(T)(不考虑 vtable 指针额外开销) - 构造失败时,
placement new不释放内存,也不会自动调用析构函数——异常安全需手动处理
示例:
char storage[sizeof(std::string)];
std::string* s = new (storage) std::string("hello"); // OK
// ... 使用 s
s->~string(); // 必须显式析构为什么不能直接 delete,析构要手动调用
placement new 没有对应内置的 placement delete —— 即使构造抛异常,C++ 标准只规定会尝试调用匹配的 operator delete(void<em>, void</em>)(若存在),但默认没有定义,也不会释放你传入的 buf。
所以:
- 对象生命周期结束前,必须显式调用
p->~T() - 若
T是数组类型(如T[]),需逐个析构,或用std::destroy(C++17 起) - 不能对
p调用delete p或delete[] p,那会触发未定义行为(因为内存不是由new分配的)
容易踩的坑:
- 忘记析构 → 资源泄漏(如
std::string内部堆内存没释放) - 在析构后再次访问
p→ 悬空指针 - 把
placement new和malloc/free混用却忽略对齐(尤其在 ARM 或 AVX 场景下)
实际用在哪?哪些场景绕不开 placement new
典型使用场景包括:
- 内存池 / 对象池实现:预分配一大块内存,反复构造/析构同类型对象
-
std::vector和std::string的内部缓冲区管理(小字符串优化除外) - 无堆环境(嵌入式、内核模块)中避免动态分配
- 序列化反序列化:从字节流中“复活”对象到指定地址
性能影响:
- 避免 malloc/free 开销,构造速度更快
- 但失去内存分配器的碎片整理能力,长期运行可能因局部性差导致 cache miss 增多
兼容性注意:
- C++17 起,
std::allocator_traits::construct默认使用placement new,但自定义 allocator 可覆盖 - 如果重载了类专属的
operator new(size_t, void*),会被优先调用(可用于注入日志或验证逻辑)
对齐是最容易被忽略的复杂点。用 std::aligned_storage_t 或 std::aligned_alloc(C++17)比手算更可靠。


















