STL 分配器將對象銷毀分為兩步:析構 與 內存釋放,分別對應 destroy() 和 deallocate();而 delete 表達式一次性完成兩者。
| 操作 | 功能 | 層級 | 典型實現 |
|---|---|---|---|
destroy(p) | 調用對象析構函數,釋放對象內部資源 | 分配器成員 / std::allocator_traits | p->~T() |
deallocate(p, n) | 歸還原始內存塊給分配器/系統,不調用析構 | 分配器成員 | ::operator delete(p) 或內存池回收 |
delete p | 先析構,後釋放內存(單對象) | 語言運算符 | p->~T(); ::operator delete(p); |
delete[] p | 逐個析構陣列元素,釋放整塊內存 | 語言運算符 | 循環析構 + ::operator delete(p) |
struct A { char* buf; A() { buf = new char[1000000]; } ~A() { delete[] buf; } };
int main() {
std::allocator alloc;
for (;;) {
A* p = new A;
alloc.deallocate(p, 1); // 僅釋放 A 對象內存,未調用 ~A() → buf 洩漏
}
}
正確做法:
alloc.destroy(p); // 調用 ~A() 釋放 buf alloc.deallocate(p, 1); // 釋放 p 本身 // 或直接 delete p; // 編譯器自動生成上述兩步
// vector 析構簡化邏輯
~vector() {
for (auto it = begin(); it != end(); ++it)
alloc.destroy(&*it); // 1. 析構元素
alloc.deallocate(data_, capacity()); // 2. 釋放緩衝區
}
::operator delete:全局釋放函數,僅釋放內存,不調用析構。deallocate:分配器封裝,可能走內存池,不調用析構。destroy:僅調用析構,不釋放內存。delete:語言級運算符,原子性完成析構+釋放,不可分割。destroy/deallocate。construct/destroy 和 allocate/deallocate。destroy 負責「對象級清理」,deallocate 負責「內存級回收」,delete 將兩者綁定。STL 分配器分離二者以支持內存池、對象池等高級優化。應用層代碼應優先使用 delete、智能指針或容器,避免手動拆分導致錯誤。