問題場景
char* pn = new char[100]; // 帶 [] 分配陣列
char* name = pn; // 不帶 [] 指標指向同一記憶體
// 使用 name...
// 釋放時:delete[] pn; 還是 delete name; 還是 delete[] name;
核心規則
- 配對原則:
new T 配 delete;new T[N] 配 delete[]。
- 釋放責任在「分配者」:誰呼叫
new[],誰負責呼叫 delete[]。其他指標只是別名,不可再次 delete。
- 指標類型不影響釋放方式:
char*、char(*)[100]、void* 指向同一塊記憶體,釋放方式取決於原始 new 運算式。
正確做法
char* pn = new char[100]; // 分配陣列 -> 必須用 delete[]
char* name = pn; // 別名
// 使用完畢
delete[] pn; // 正確:配對 new[]
pn = nullptr;
name = nullptr; // 避免懸空指標
// 錯誤示範
delete name; // 錯誤:未配對 new[],行為未定義(通常崩潰或記憶體洩漏)
delete[] name; // 雖然編譯通過,但語意上 name 非分配者,易誤導維護者
為何 delete name 會出錯
delete 單一物件時,編譯器不會呼叫陣列元素的解構函式(對 primitive type 無影響),但記憶體管理器需要知道塊大小。new[] 通常在塊頭記錄元素個數,delete[] 會讀取該資訊正確歸還。混用會導致堆損壞。
- 對非 POD 類型(如
std::string, 自訂類別),delete 只呼叫第一個元素的解構函式,其餘元素資源洩漏。
現代 C++ 避免手動管理
// 推薦:std::string 或 std::vector
std::string name = "hello"; // 自動管理
std::vector buffer(100); // 自動管理陣列
// 若需 C API 互操作
std::unique_ptr pn(new char[100]); // 自動 delete[]
char* name = pn.get(); // 取得原始指標傳給 C 函式
// 離開作用域自動釋放,無需手動 delete