RAII

RAII，全稱資源獲取即初始化（Resource Acquisition Is Initialization），它是在一些面嚮對象語言中的一種Programming idiom。RAII源於C++，在Java，C#，D，Ada，Vala和Rust中也有應用。1984-1989年期間，比雅尼·斯特勞斯特魯普和Andrew Koenig (programmer)在設計C++異常時，為解決Resource management (computing)時的Exception safety性而使用了該用法，後來比雅尼·斯特勞斯特魯普將其稱為RAIIps=. I called this technique “resource acquisition is initialization.”。
RAII要求，資源的有效期與持有資源的Object lifetime嚴格綁定，即由對象的構造函數完成Resource allocation (computer)（獲取），同時由析構函數完成資源的釋放。在這種要求下，只要對象能正確地析構，就不會出現資源洩漏問題。

**作用**
RAII的主要作用是在不失代碼簡潔性的同時，可以很好地保證代碼的異常安全性。
下面的C++實例說明了如何用RAII訪問文件和互斥量：
#include 
#include 
#include 
#include 
#include 

void write_to_file(const std::string & message)
{
// 創建關於文件的互斥鎖
static std::mutex mutex;

// 在訪問文件前進行加鎖
std::lock_guard lock(mutex);

// 嘗試打開文件
std::ofstream file("example.txt");
if (!file.is_open())
throw std::runtime_error("unable to open file");

// 輸出文件內容
file 
C++保證了所有棧對象在生命週期結束時會被銷燬(即調用析構函數)loc=chpt. 14.4.1 Using Constructors and Destructors，所以該代碼是異常安全的。無論在write_to_file函數正常返回時，還是在途中拋出異常時，都會引發write_to_file函數的堆棧回退，而此時會自動調用lock和file對象的析構函數。
當一個函數需要通過多個局部變量來管理資源時，RAII就顯得非常好用。因為只有被構造成功(構造函數沒有拋出異常)的對象才會在返回時調用析構函數loc=chpt. 14.4.1 Using Constructors and Destructors，同時析構函數的調用順序恰好是它們構造順序的反序，這樣既可以保證多個資源(對象)的正確釋放，又能滿足多個資源之間的依賴關係。
由於RAII可以極大地簡化資源管理，並有效地保證程序的正確和代碼的簡潔，所以通常會強烈建議在C++中使用它。

**典型用法**
RAII在C++中的應用非常廣泛，如C++標準庫中的lock_guard便是用RAII方式來控制互斥量:
template  class lock_guard {
private:
Mutex& mutex_;
public:
lock_guard(Mutex& mutex) : mutex_(mutex) { mutex_.lock(); }
~lock_guard() { mutex_.unlock(); }
lock_guard(lock_guard const&) = delete;
lock_guard& operator=(lock_guard const&) = delete;
};
程序員可以非常方便地使用lock_guard，而不用擔心異常安全問題
extern void unsafe_code();  // 可能拋出異常
using std::mutex;
using std::lock_guard;
mutex g_mutex;
void access_critical_section()
{
lock_guard lock(g_mutex);
unsafe_code();
}
實際上，C++標準庫的實現就廣泛應用了RAII，典型的如容器、智能指針等。

**RRID**
RAII還有另外一種被稱為RRID(Resource Release Is Destruction)的特殊用法loc=chpt. 3.4 RRID，即在構造時沒有“獲取”資源，但在析構時釋放資源。ScopeGuard和Boost.ScopeExit就是RRID的典型應用：
#include 
class ScopeGuard {
private:
typedef std::function destructor_type;
destructor_type destructor_;
bool dismissed_;
public:
ScopeGuard(destructor_type destructor) : destructor_(destructor), dismissed_(false) {}
~ScopeGuard()
{
if (!dismissed_) {
destructor_();
}
}
void dismiss() { dismissed_ = true; }
ScopeGuard(ScopeGuard const&) = delete;
ScopeGuard& operator=(ScopeGuard const&) = delete;
};
ScopeGuard通常用於省去一些不必要的RAII封裝，例如
void foo()
{
auto fp = fopen("/path/to/file", "w");
ScopeGuard fp_guard(&fp() { fclose(fp); });
write_to_file(fp);                     // 異常安全
}
在D語言中，scope關鍵字也是典型的RRID用法，例如
void access_critical_section()
{
Mutex m = new Mutex;
lock(m); 
scope(exit) unlock(m);
unsafe_code();                  // 異常安全
}
Resource create()
{
Resource r = new Resource();
scope(failure) close(f);
preprocess(r);                  // 拋出異常時會自動調用close(r)
return r;
}

**與finally的比較**
雖然RAII和finally都能保證資源管理時的異常安全，但相對來說，使用RAII的代碼相對更加簡潔。
如比雅尼·斯特勞斯特魯普所說，“在真實環境中，調用資源釋放代碼的次數遠多於資源類型的個數，所以相對於使用finally來說，使用RAII能減少代碼量。”
例如在Java中使用finally來管理Socket資源
void foo() {
Socket socket;
try {
socket = new Socket();
access(socket);
} finally {
socket.close();
}
}
在採用RAII後，代碼可以簡化為
void foo() {
try (Socket socket = new Socket()) {
access(socket);
}
}
特別是當大量使用Socket時，重複的finally就顯得沒有必要。

**參考資料**
 dead-url=no- dead-url= no- dead-url=no