C++ 的術語有時令人困惑,例如 new operator(new 操作符)與 operator new 的區別。兩者僅一字之差,卻代表完全不同的機制。搞混它們可能導致記憶體管理錯誤。本文將釐清這些概念,並探討 placement new 以及陣列 new/delete 的細節。
當你撰寫 string *ps = new string("Memory Management"); 時,使用的是 new 操作符。它如同 sizeof 一樣是語言內建關鍵字,無法改變其行為。它的工作固定包含兩步:
operator new 配置足夠的原始記憶體。編譯器產生的代碼大致相當於:
void *memory = operator new(sizeof(string)); // 1. 配置原始記憶體
new (memory) string("Memory Management"); // 2. 呼叫建構函式 (placement new 語法)
string *ps = static_cast<string*>(memory); // 3. 指標指向新物件
注意:程式設計師不能直接呼叫建構函式來初始化堆上物件,這是編譯器的專屬權利。
operator new 是一個可被重載的函數,負責「僅配置記憶體」,不負責建構。標準宣告如下:
void* operator new(std::size_t size) noexcept(false);
void* 指向未初始化的原始記憶體。size_t,可增加額外參數形成重載(如 placement new)。malloc,它不知建構函式為何物。你通常不直接呼叫它,但若直接呼叫:void* raw = operator new(sizeof(string)); 只會給你一塊夠大的記憶體,裡面沒有 string 物件。
有時我們已擁有一塊記憶體(如共享記憶體、memory-mapped I/O、預先配置的緩衝區),想在上面建構物件。這時使用 placement new:
Widget* constructWidgetInBuffer(void* buffer, int widgetSize) {
return new (buffer) Widget(widgetSize); // 傳遞額外參數 buffer
}
對應的 operator new 簽名為:
void* operator new(std::size_t, void* location) noexcept {
return location; // 直接回傳呼叫者提供的位址
}
需包含 <new> 標頭。Placement new 不配置記憶體,只負責在既有記憶體上呼叫建構函式。
配置陣列時:string* ps = new string[10];
new operator,但配置記憶體改呼叫 operator new[](array new)。delete[] ps; 會對每個元素呼叫解構函式,再呼叫 operator delete[] 釋放記憶體。operator new[] 和 operator delete[] 同樣可重載,允許自訂陣列記憶體管理。早期編譯器若不支援,會退回使用全域 operator new,導致自訂困難。
| 名稱 | 性質 | 可否改變行為 | 主要職責 |
|---|---|---|---|
| new operator | 語言關鍵字 | 否 | 配置記憶體 + 呼叫建構函式 |
| operator new | 可重載函數 | 是 | 僅配置原始記憶體 |
| placement new | 特定重載形式 | 是 (標準庫提供) | 在指定位址建構物件 |
| operator new[] | 可重載函數 | 是 | 配置陣列原始記憶體 |
| delete operator | 語言關鍵字 | 否 | 呼叫解構函式 + 釋放記憶體 |
| operator delete | 可重載函數 | 是 | 僅釋放原始記憶體 |
核心原則:你無法改變 new/delete 操作符「做什麼」(配置+建構/解構+釋放),但可透過重載 operator new/delete 改變它們「怎麼做」(記憶體配置/釋放策略)。