RAII (Resource Acquisition Is Initialization),中文常譯為「資源取得即初始化」。核心思想:利用物件的建構與解構函數,將資源的生命週期綁定至物件的生命週期。物件存在 → 資源有效;物件銷毀 → 資源釋放。這對於非記憶體資源(檔案、鎖、Socket、資料庫連線等)尤為關鍵。
void f() {
A* a = new A();
B* b = new B();
C* c = new C();
if (error) {
delete a; delete b; delete c; // 易遺漏、重複、順序錯誤
return;
}
Z* z = new Z();
try { f2(); } // 可能拋出異常
catch(...) { delete a; delete b; delete c; /* 忘記 delete z */ }
}
即使資深工程師也難保證所有路徑都正確釋放。
void f() {
auto_ptr a(new A());
auto_ptr b(new B());
auto_ptr c(new C());
if (error) return; // 自動釋放
auto_ptr z(new Z());
try { f2(); }
catch(...) { /* 自動釋放所有 */ }
}
FILE* pf1 = fopen("log1.txt", "w");
FILE* pf2 = fopen("log2.txt", "w");
// ...
try { f2(); }
catch(...) { fclose(pf1); fclose(pf2); /* 又回到醜陋代碼 */ }
class File_handle {
FILE* p;
public:
File_handle(const char* name, const char* mode) {
p = fopen(name, mode);
if (!p) throw Cannot_open(name);
}
File_handle(const string& name, const char* mode) {
p = fopen(name.c_str(), mode);
if (!p) throw Cannot_open(name);
}
~File_handle() { fclose(p); } // 保證釋放
// 禁止複製(或實作移動語意)
File_handle(const File_handle&) = delete;
File_handle& operator=(const File_handle&) = delete;
int GetChar() { return fgetc(p); } // 此處 p 必定有效
};
void f(string s) {
File_handle file(s, "r"); // 建構時取得資源
// 使用 file...
} // 離開作用域自動呼叫 ~File_handle() → fclose()
void f() {
auto_ptr pf1(new File_handle("log1.txt", "w"));
auto_ptr pf2(new File_handle("log2.txt", "w"));
auto_ptr pf3(new File_handle("log3.txt", "w"));
try { f2(); }
catch(...) { /* 全自動釋放 */ }
}
class BadFile {
FILE* p;
public:
BadFile() : p(NULL) {}
void Open(...) { if(p) throw; p=fopen(...); if(!p) throw; }
void Close() { if(p) { fclose(p); p=NULL; } else throw; }
int GetChar() { if(!p) throw; return fgetc(p); }
};
每次呼叫都需檢查、處理異常,極易出錯且碎片化業務邏輯。
| 資源類型 | 標準 RAII 封裝 |
|---|---|
| 動態記憶體 | unique_ptr, shared_ptr |
| 檔案流 | fstream, ifstream, ofstream |
| 鎖 | lock_guard, unique_lock |
| 自訂資源 | 自行實作符合 RAII 的類別 |
RAII 是達到 強烈異常安全保證 的基石:操作要么成功完成,要么拋出異常並保證資源不洩漏、狀態不破壞。
RAII 不只是技巧,而是 C++ 資源管理的核心哲學。掌握它,意味著告別手動 new/delete、fopen/fclose、Lock/Unlock 的混亂,擁抱「建構即獲取、解構即釋放」的優雅與安全。現代 C++ (C++11 起) 透過移動語意與標準庫智慧指標,讓 RAII 更易用、更零成本。