動態內存與手動管理痛點
堆內存(`new`/`delete`)需手動釋放,易發生洩漏(忘記 delete)、二次釋放、懸空指針。異常拋出導致釋放代碼被跳過更是隱患。智能指針通過 RAII(資源獲取即初始化)自動管理生命週期。
shared_ptr:共享所有權
- 引用計數機制:拷貝/賦值遞增,析構遞減,歸零時 `delete` 對象。
- `make_shared(args)`:單次分配對象+控制塊,異常安全,性能優於 `new T + shared_ptr`。
- 自定義刪除器:`shared_ptr(ptr, deleter)`,管理非 `new` 內存(如 `malloc`、C API 句柄、文件描述符)。
- `get()` 僅供傳遞給不識別智能指針的舊代碼,**不可**用於初始化另一個智能指針。
- 線程安全:控制塊引用計數原子操作,但對象訪問非線程安全。
unique_ptr:獨占所有權
- 不可拷貝,可移動(`std::move`)。轉移所有權:`up2 = std::move(up1)`。
- 無 `make_unique`(C++14 引入),直接 `unique_ptr(new T)`。
- 自定義刪除器作為模板參數:`unique_ptr`,無運行期開銷(空基類優化)。
- `release()` 放棄所有權返回裸指針,`reset()` 釋放/重置。
- 零開銷抽象:大小等同裸指針,釋放調用 `delete`/`delete[]` 內聯。
weak_ptr:弱引用打破循環
- 不增加引用計數,指向 `shared_ptr` 管理對象。
- `lock()` 返回 `shared_ptr`(對象存活)或空(已釋放),原子操作。
- 用於緩存、觀察者模式、父子雙向引用(子持 `weak_ptr` 父)。
- `expired()` / `use_count()` 輔助檢查。
動態數組管理
- `unique_ptr`:專用部分特化,支持 `operator[]`,析構調用 `delete[]`。無 `operator*`/`->`。
- `shared_ptr` (C++17) / `shared_ptr(new T[n], [](T* p){ delete[] p; })` 自定義刪除器。
- 優先用 `vector` / `string` 替代手動動態數組。
allocator:底層內存分配器
分離「分配原始內存」與「構造對象」:
- `allocate(n)`:獲取 n 個 T 大小的未構造內存塊。
- `construct(p, args)`:就地構造(placement new)。
- `destroy(p)`:顯式調用析構函數。
- `deallocate(p, n)`:歸還內存。
標準容器內部使用分配器。算法 `uninitialized_copy/fill` 配合分配器實現高效內存操作(如 `vector::reserve`/`resize`/`insert`)。
核心規範(C++ Core Guidelines)
- 優先 `make_unique` / `make_shared` 創建。
- 函數參數傳 `widget&` / `const widget&` / `widget*`(非所有權),所有權轉移用 `unique_ptr` / `shared_ptr` by value。
- 不 `delete get()` 返回指針。
- 不 `shared_ptr` 管理非 `new` 內存無自定義刪除器。
- 循環引用用 `weak_ptr` 打破。
總結
智能指針是現代 C++ 資源管理基石。`unique_ptr` 獨占零開銷,`shared_ptr` 共享線程安全計數,`weak_ptr` 打破循環。配合 `make_*` 工廠、自定義刪除器、動態數組特化、allocator 底層支持,構建完整、安全、高效的內存管理體系。徹底告別手動 `new`/`delete`,編寫無洩漏、異常安全代碼。