RAII(Resource Acquisition Is Initialization) 是 C++ 最具標誌性的資源管理慣用法,由 Bjarne Stroustrup 提倡,核心思想極簡:將資源生命週期綁定至物件生命週期,由建構函數獲取資源、解構函數釋放資源,利用 C++ 確定性的物件銷毀機制,實現「有始有終」的自動化資源管理。
new/delete 易在分支、異常、提前返回處遺漏釋放。try/finally。class Resource {
Handle _res;
public:
explicit Resource(const char* name) : _res(alloc_resource(name)) {}
~Resource() { release_resource(_res); }
// 禁止複製,或實作移動語意(C++11)
Resource(const Resource&) = delete;
Resource& operator=(const Resource&) = delete;
Resource(Resource&& rhs) noexcept : _res(rhs._res) { rhs._res = nullptr; }
Resource& operator=(Resource&& rhs) noexcept {
if (this != &rhs) { release_resource(_res); _res = rhs._res; rhs._res = nullptr; }
return *this;
}
Handle get() const { return _res; }
};
// 使用:作用域即生命週期
void transaction(const char* name) {
Resource res(name); // 獲取
// ... 使用 res.get() ...
} // 離開作用域自動釋放,即使拋出異常
std::unique_ptr:獨佔所有權,零開銷。std::shared_ptr:引用計數共享所有權,解決「誰負責刪除」難題。std::weak_ptr:打破循環引用。現代 C++ 原則:絕不直接使用 new/delete,永遠以智慧指標或容器封裝。
class Transaction {
Resource _res1, _res2;
Connection _conn;
public:
Transaction() : _res1("A"), _res2("B"), _conn(open_db()) {}
void execute() { /* 業務邏輯 */ }
~Transaction() { /* 解構順序自動釋放 _conn, _res2, _res1 */ }
};
資源成為類別成員,隨父物件同生共死,邏輯清晰無洩漏。
工作佇列持有 std::function 或繼承 Task 介面的物件,任務物件內部以 RAII 管理所需資源;事件迴圈只需驅動任務執行,資源釋放自動完成。
| 維度 | RAII (C++) | GC (Java/C#/Go) |
|---|---|---|
| 資源類型 | 記憶體 + 所有系統資源 | 僅記憶體 |
| 釋放時機 | 確定性(離開作用域) | 非確定性(GC 掃描時) |
| 效能開銷 | 極低(零成本抽象) | 需保留 2x 記憶體、CPU 週期 |
| 即時系統適用 | 是(可預測延遲) | 否(GC 停頓不可控) |
| 程式設計師紀律 | 需遵守「不裸用指標」 | 自動但易誤用非記憶體資源 |
std::unique_ptr 暫存再轉移)。std::vector 後再 reserve/push_back 導致移動失敗(確保移動建構 noexcept)。RAII 不只是技巧,而是 C++ 「零開銷抽象」哲學的集中體現。掌握 RAII,即掌握了 C++ 資源管理的靈魂。將其內化為肌肉記憶,配合現代智慧指標與標準庫容器,能讓你專注於業務邏輯,而非與資源洩漏搏鬥。