什么是placement_new?
昨天面试完,感觉自己有好多不足啊,亡羊补牢,女娲补天,哈哈哈
在 C++ 中,placement new 是一种特殊的 new
运算符变体,允许在预分配的内存地址上构造对象,而不分配新的内存。与标准 new
不同,placement new 仅负责调用构造函数,不负责分配内存。这使得它在需要精确控制内存分配的场景(如内存池、自定义容器或嵌入式系统)中非常有用。
1. 定义
Placement new 是 new
运算符的一种重载形式,其核心思想是将对象的构造与内存分配分离。标准 new
运算符的工作流程是:
- 通过
operator new
分配内存。 - 调用对象的构造函数初始化内存。
而 placement new 跳过第一步,直接在用户提供的内存地址上调用构造函数。其标准语法定义在 <new>
头文件中:
1 | void* operator new(std::size_t size, void* ptr) noexcept; |
size
:要构造的对象大小(通常由编译器自动传递)。ptr
:用户提供的内存地址,用于构造对象。
2. 语法
Placement new 的基本语法如下:
1 | new (address) Type(constructor_args) |
address
:指向预分配内存的指针,必须有足够大小且正确对齐。Type
:要构造的类型。constructor_args
:传递给构造函数的参数(可选)。
3. 工作原理
- 内存分配:用户必须提供一块足够大小且正确对齐的内存(通常通过
new
、malloc
或静态分配)。 - 对象构造:Placement new 调用指定类型的构造函数,在提供的内存地址上构造对象。
- 内存管理:Placement new 不负责分配或释放内存,内存的分配和释放由用户管理。
- 析构:Placement new 不自动调用析构函数,用户需要手动调用
obj->~Type()
来销毁对象。
4. 关键特性
- 不分配内存:仅调用构造函数,内存必须预先分配。
- 灵活性:允许在任意内存位置构造对象(如栈、堆或自定义内存池)。
- 性能优化:避免动态内存分配的开销,适用于高性能场景。
- 手动管理:用户负责内存分配、释放和对象析构,增加了代码复杂性。
5. 使用场景
Placement new 在以下场景中特别有用:
- 内存池
- 在预分配的内存池中构造对象,避免频繁的动态内存分配,减少内存碎片。
- 示例:游戏引擎或实时系统中使用内存池管理对象。
- 自定义容器
- 标准库容器(如
std::vector
)使用 placement new 在预分配的缓冲区上构造元素。 - 示例:
std::vector
的push_back
可能在预分配的数组上使用 placement new 构造新元素。
- 标准库容器(如
6. 注意事项
使用 placement new 时需格外小心,以下是关键注意点:
内存大小:提供的内存必须足够容纳对象(
sizeof(Type)
)。内存对齐
内存地址必须满足类型的对齐要求(
alignof(Type)
)。C++11 引入的
std::aligned_alloc
或std::align
可帮助对齐内存。示例:
1
2void* raw = std::aligned_alloc(alignof(MyClass), sizeof(MyClass));
MyClass* obj = new(raw) MyClass();
析构函数:Placement new 不自动调用析构函数,必须手动调用
obj->~Type()
。内存释放:用户负责释放原始内存,需确保在对象析构后正确释放。
异常安全
- 如果构造函数抛出异常,需确保内存不会泄漏。
- 使用智能指针或 RAII 机制管理内存可提高安全性。
不可重复构造:在同一内存地址上重复调用 placement new(未先析构)会导致未定义行为。
7. 高级示例:实现简单内存池
以下是一个使用 placement new 实现简单内存池的例子:
1 |
|
输出:
1 | Constructed with 10 |
All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
Comments