placement new 是 C++ 中一种特殊的 new 操作符形式。它允许在已分配的内存区域按指定地址放置对象。通常用于手动内存管理,是分配器、对象池和在固定缓冲区中序列化的基础。
语法:
#include <new> void* memory = malloc(sizeof(MyClass)); MyClass* obj = new (memory) MyClass(args...); // 在内存地址处调用构造函数
这里内存单独分配(例如,通过 malloc 或分配器),然后在该内存中调用类构造函数。请不要忘记显式调用析构函数:
obj->~MyClass(); free(memory);
优点:
是否可以使用相同的内存块通过 'placement new' 依次放置多个对象,且会导致什么后果?
常见错误回答是可以在内存中放置新对象,而不关心以前状态。实际上,如果在同一内存区域上放置新对象而不调用其析构函数,资源将泄漏,随后调用析构函数将导致未定义行为(UB)。
示例:
void* place = malloc(sizeof(A)); A* a = new (place) A(); B* b = new (place) B(); // 对象 A 未销毁!UB!
故事
在高负载系统中,开发人员通过
malloc+placement new实现了对象池,但在将对象返回到池时忘记调用析构函数。结果资源(对象内部的系统描述符)未释放,导致泄漏和服务器“挂起”。
故事
在序列化二进制数据的项目中,使用缓冲区和
placement new在原地解码对象。然而,在放置之前忘记将内存初始化为零,导致读取未初始化的数据,并在处理 POD 结构时出现奇怪的错误。
故事
一个模块迁移到了通过
placement new放置对象数组到预先分配的内存块中。在类内部添加了具有非平凡析构函数的新成员后,程序意外终止 — 忘记现在需要在清理时显式调用每个元素的析构函数,而以前不需要。