new 表達式完成兩件事:調用 operator new 分配原始內存,然後調用構造函數初始化對象。delete 反之:先調用析構函數,再調用 operator delete 釋放內存。
void* operator new(std::size_t size); // 單對象分配 void operator delete(void* ptr) noexcept; // 單對象釋放 void* operator new[](std::size_t size); // 數組分配 void operator delete[](void* ptr) noexcept; // 數組釋放
malloc/free,但可重載、可拋異常(bad_alloc)。void* p = operator new(sizeof(Foo));。Foo* p = new Foo(10); // 單對象 // 等價於: void* raw = operator new(sizeof(Foo)); Foo* p = new(raw) Foo(10); // placement new 調用構造函數
delete p; // 等價於: p->~Foo(); // 顯式調用析構函數 operator delete(p); // 釋放內存
new Foo[3]:分配 3*sizeof(Foo) + 額外空間(通常 4/8 字節)存儲數組長度(cookie),調用 3 次構造函數,返回首元素地址(cookie 之後)。delete[] p:讀取 cookie 獲取長度 N,逆序調用 N 次析構函數,釋放整個塊(含 cookie)。new[] 必須配 delete[],new 配 delete。混用導致析構次數錯誤、內存釋放地址錯誤、崩潰。int、double)無析構函數,delete p 僅調用 operator delete(p)。若實現中 new[] 未存 cookie(某些編譯器對 POD 類型優化),則 p 指向塊首,delete 能正確釋放。但不可依賴,屬未定義行為。delete p 只會析構第 1 個對象,其餘洩漏;且釋放地址錯誤(未扣除 cookie),堆損壞必現。char buffer[sizeof(Foo)]; Foo* p = new(buffer) Foo(10); // 在預備緩衝區構造對象 p->~Foo(); // 手動調用析構,不釋放 buffer 內存
| 代碼 | 後果 |
|---|---|
new Foo[10]; delete p; | 僅析構 1 個,釋放地址偏移 → 崩潰/洩漏 |
new Foo; delete[] p; | 讀取不存在的 cookie → 未定義行為 |
| 忘記定義聲明的構造/析構 | 鏈接錯誤(如 AAA a; 報錯,new AAA 不報直到 delete) |
| 基類析構非 virtual | 刪除基類指針指向派生對象 → 派生析構未調用 → 資源洩漏 |
unique_ptr, shared_ptr)和容器,避免裸 new/delete。new/delete、new[]/delete[]。