應用場景
- 自訂記憶體池/垃圾收集器:大塊預分配,物件在池內構造/銷毀,避免碎片與分配開銷
- 即時系統:分配失敗風險不可接受,預留足夠緩衝
- 物件重用:GUI 對話框、遊戲實體等頻繁建構/銷毀,固定緩衝區反覆使用
核心步驟
- 分配原始位元組緩衝:
char* buf = new char[sizeof(Foo)];
- Placement new 構造:
Foo* p = new (buf) Foo; (需 #include )
- 正常使用物件:
p->method();
- 顯式呼叫解構函數:
p->~Foo();
- 釋放緩衝:
delete[] buf;
完整範例
#include
#include
class Foo {
int id_;
public:
Foo(int id = 0) : id_(id) { std::cout << "Foo(" << id_ << ") ctor\n"; }
~Foo() { std::cout << "Foo(" << id_ << ") dtor\n"; }
void greet() { std::cout << "Hello from " << id_ << "\n"; }
};
void placement_demo() {
// 1. 預分配緩衝 (足夠最大物件)
char* buf = new char[sizeof(Foo)];
// 2. 在緩衝構造物件
Foo* p1 = new (buf) Foo(42);
p1->greet();
// 3. 顯式解構
p1->~Foo();
// 4. 同一緩衝構造不同型別 (假設 Bar 大小 <= Foo)
// Bar* p2 = new (buf) Bar(...);
// p2->~Bar();
// 5. 釋放緩衝
delete[] buf;
}
int main() {
placement_demo();
return 0;
}
輸出
Foo(42) ctor
Hello from 42
Foo(42) dtor
關鍵細節與陷阱
| 議題 | 說明 |
| 對齊需求 | 緩衝位址必須滿足型別對齊。C++17 起可用 std::aligned_storage 或 alignas 保證 |
| 陣列 Placement New | new (buf) Foo[N] 需手動逐個解構,極易出錯,建議避免 |
| 例外安全 | 建構拋出異常時,緩衝未構造成功,無需解構;但若部分構造成功需手動回滾 |
| 解構順序 | 與構造順序相反 (LIFO),手動管理時必須遵守 |
| delete vs 解構函數 | delete p 會釋放記憶體,**不可用於 placement new 物件**;必須分離「解構」與「釋放記憶體」 |
現代 C++ 封裝:記憶體池雛形
#include
#include
template
class ObjectPool {
std::vector> storage_;
std::vector in_use_;
public:
ObjectPool(size_t capacity) : storage_(capacity), in_use_(capacity, false) {}
template
T* acquire(Args&&... args) {
for (size_t i = 0; i < storage_.size(); ++i) {
if (!in_use_[i]) {
in_use_[i] = true;
T* obj = new (&storage_[i]) T(std::forward(args)...);
return obj;
}
}
return nullptr; // 或擴容
}
void release(T* obj) {
// 計算索引
char* base = reinterpret_cast(storage_.data());
char* ptr = reinterpret_cast(obj);
size_t idx = (ptr - base) / sizeof(storage_[0]);
if (idx < in_use_.size() && in_use_[idx]) {
obj->~T();
in_use_[idx] = false;
}
}
};
標準庫支援
std::allocator_traits::construct/destroy 封裝 placement new / 解構呼叫
std::optional, std::variant 內部即使用 placement new 管理內建儲存
std::pmr::polymorphic_allocator (C++17) 整合記憶體資源與 placement new
總結
Placement new 將記憶體分配與物件構造分離,是高效能記憶體管理、物件池、即時系統的核心技術。關鍵紀律:**每個 placement new 必須配對一個顯式解構函數呼叫**,且記憶體釋放與物件解構完全分離。現代 C++ 建議封裝於 RAII 容器 (如上述 ObjectPool) 避免手動管理錯誤。
- 上一篇文章: C++ 模板 入門到進階:語法、技巧、繼承、靜態成員與泛型程式設計哲學
- 下一篇文章: C++ 專案記憶體缺陷預防與檢測全方位策略:編碼規範、靜態分析、執行期工具、除錯技巧與崩潰報告
Copyright ©
程式碼之家 All Rights Reserved