placement new 是在已分配内存上显式调用构造函数的机制,不分配内存;它不是通用 new 替代方案,需手动配对析构,且不能直接用于 vector 或 String 底层内存。

placement new 是什么,不是什么
它不是用来替代 new 的通用方案,而是仅在你已有一块确定地址的内存(比如栈上、共享内存、预分配缓冲区)时,显式调用构造函数的机制。它不分配内存,只负责初始化——这点常被误认为“能省掉堆分配”,其实漏掉析构就容易出事。
- 常见错误现象:
std::bad_alloc没了,但对象行为异常、析构函数完全没被调用、多次delete崩溃 - 典型使用场景:实现内存池、嵌入式固定缓冲区、避免 STL 容器内部重复分配
- 关键区别:
new (ptr) T不会调用 operator new,也不会记录分配元信息;delete ptr会崩溃,必须手动调用析构 + 不调用 delete
怎么安全地用 placement new 构造和析构
构造只是半步,析构必须手动配对,否则资源泄漏或未定义行为是大概率事件。c++ 不会自动跟踪哪些对象是 placement new 出来的。
- 构造写法:
char buffer[sizeof(MyClass)]; MyClass* obj = new (buffer) MyClass(42); - 析构必须显式:
obj->~MyClass();—— 注意不是delete obj,也不是obj->~MyClass()后再delete[] buffer(除非 buffer 本身是 new 出来的) - 如果类型有虚析构函数,
~MyClass()仍有效;但若 buffer 在栈上,别忘了 buffer 生命周期比对象长 - 性能影响:零分配开销,但失去 RAII 自动管理,调试难度上升;兼容性无问题,所有标准 C++ 编译器都支持
为什么 operator delete(void*, void*) 不会被调用
因为 placement new 对应的 operator new 形参版本(void* operator new(size_t, void* ptr))是 no-op,它不申请内存,所以也不存在“匹配的 delete”可调用。编译器不会自作主张补一个。
- 常见错误:重载了全局
operator new(size_t, void*),却忘了也得提供operator delete(void*, void*)(即使空实现),否则链接失败 - 更隐蔽的坑:如果你自定义了类专属的
operator new(size_t, void*),但没配operator delete(void*, void*),而该类对象又抛异常,C++ 会尝试调用后者做清理——找不到就直接终止 - 建议:只要用了 placement new,就同步提供对应的 placement delete,哪怕只写
void operator delete(void*, void*) noexcept {}
vector 或 string 能不能直接 placement new 进去
不能。标准容器内部有自己的内存管理逻辑,它们的构造函数假设自己控制底层存储。直接在 vector 内存上调用 new (&v[0]) T 会破坏其状态,触发未定义行为。
立即学习“C++免费学习笔记(深入)”;