plac++ement new 是 c++ 中用于在指定内存位置构造对象的机制,不进行内存分配。它允许使用已有内存构建对象,常用于内存池、嵌入式系统等需精细控制内存的场景。其标准形式为 void operator new(size_t, void ptr),返回传入的指针 ptr。构造对象数组时需手动操作,步骤如下:1. 分配足够大的原始内存块;2. 按对象大小和数量计算偏移量;3. 对每个位置依次调用 placement new。例如可使用 char 数组或 malloc 分配内存,再通过循环构造多个对象。对于仅有带参构造函数的类,也可在构造时传递参数。使用后必须显式调用析构函数并释放内存,避免资源泄漏。此外还需注意内存对齐、异常安全等问题。

在C++中,placement new 是一种特殊的 new 表达式,它允许我们在已经分配好的内存中构造对象。这种机制在一些对内存控制要求较高的场景(如内存池、嵌入式系统或自定义容器)中非常有用。

当我们需要构造一个对象数组,并且希望显式地控制每个元素的构造方式时,placement new 就派上用场了。

什么是placement new?
简单来说,placement new 并不会分配新的内存,而是使用我们提供的已有内存来构造对象。标准库提供了如下形式:
void* operator new(size_t, void* ptr) noexcept;
这个版本的 new 不会申请内存,只是返回传入的指针
ptr,用于在该地址构造对象。

例如:
char memory[sizeof(MyClass)]; MyClass* obj = new(memory) MyClass(); // 在memory中构造MyClass对象
如何用placement new构造对象数组?
如果我们想构造多个对象,比如一个对象数组,就不能直接像上面那样写。因为这样只能构造单个对象。我们需要手动遍历内存块,依次调用 placement new。
基本步骤如下:
- 分配足够大的原始内存块(可以使用 char 数组或 malloc)
- 按照对象大小和数量计算偏移量
- 对每个位置调用 placement new
示例代码:
const size_t count = 5;
char* buffer = new char[count * sizeof(MyClass)]; // 分配原始内存
for (size_t i = 0; i < count; ++i) {
MyClass* obj = new(buffer + i * sizeof(MyClass)) MyClass(); // 构造对象
}注意:这里没有自动的数组构造语法,必须自己一个个构造。
显式调用构造函数的常见场景
有时候我们不想依赖默认构造函数,或者类根本没有默认构造函数,这时候就需要显式调用特定构造函数。
比如构造参数化对象数组:
class MyClass {
public:
MyClass(int val) : value(val) {}
private:
int value;
};
// 使用placement new构造带参数的对象数组
for (size_t i = 0; i < count; ++i) {
MyClass* obj = new(buffer + i * sizeof(MyClass)) MyClass(i); // 带参数构造
}适用场景包括:
- 类型只有带参构造函数
- 需要根据索引或其他逻辑动态决定构造参数
- 构造顺序或初始化值有特殊要求
注意事项与析构处理
使用 placement new 后,不能直接 delete 指针,因为它并没有通过 new 分配内存。正确的做法是手动调用析构函数,然后释放原始内存。
析构方式:
for (size_t i = 0; i < count; ++i) {
MyClass* obj = reinterpret_cast(buffer + i * sizeof(MyClass));
obj->~MyClass(); // 显式调用析构函数
}
delete[] buffer; // 释放原始内存 几点需要注意:
- 必须手动调用析构函数,否则资源泄漏
- 内存对齐问题要确保符合类型要求
- 构造失败时要考虑异常安全,避免部分构造后程序状态混乱
基本上就这些。placement new 虽然灵活,但使用起来确实需要小心细节,尤其是在构造数组时,很多操作都要手动完成。不过对于需要精细内存管理的项目来说,这是一项必备技能。










