RAII
資源獲取即初始化(Resource Acquisition Is Initialization),簡稱 RAII,是一種 C++ 程式設計技術[1][2],它將必須在使用前獲取的資源(已分配的堆記憶體、執行執行緒、開啟的套接字、開啟的檔案、鎖定的互斥量、磁碟空間、資料庫連線——任何供應有限的資源)的生命週期與物件的生命週期繫結。
RAII 確保資源對任何可以訪問物件的函式都可用(資源可用性是類不變數,消除了冗餘的執行時測試)。它還保證當控制其生命週期的物件生命週期結束時,所有資源都以獲取的相反順序釋放。同樣,如果資源獲取失敗(建構函式因異常而退出),則所有由每個完全構造的成員和基子物件獲取的資源都以初始化順序的相反順序釋放。這利用了核心語言特性(物件生命週期、作用域退出、初始化順序和棧展開)來消除資源洩漏並保證異常安全。這種技術的另一個名稱是作用域繫結資源管理(Scope-Bound Resource Management,SBRM),因為它最基本的使用場景是 RAII 物件的生命週期因作用域退出而結束。
RAII 可以總結如下:
- 將每個資源封裝到一個類中,其中
- 建構函式獲取資源並建立所有類不變數,如果無法完成則丟擲異常,
- 解構函式釋放資源,並且從不丟擲異常;
- 始終透過 RAII 類的例項使用資源,該例項要麼
- 具有自動儲存期或臨時生命週期,要麼
- 其生命週期受自動或臨時物件的生命週期限制。
移動語義支援在物件之間、容器內部和外部以及跨執行緒傳輸資源和所有權,同時確保資源安全。 |
(C++11 起) |
帶有 open()
/close()
、lock()
/unlock()
或 init()
/copyFrom()
/destroy()
成員函式的類是非 RAII 類的典型示例。
std::mutex m; void bad() { m.lock(); // acquire the mutex f(); // if f() throws an exception, the mutex is never released if (!everything_ok()) return; // early return, the mutex is never released m.unlock(); // if bad() reaches this statement, the mutex is released } void good() { std::lock_guard<std::mutex> lk(m); // RAII class: mutex acquisition is initialization f(); // if f() throws an exception, the mutex is released if (!everything_ok()) return; // early return, the mutex is released } // if good() returns normally, the mutex is released
[編輯] 標準庫
管理自身資源的 C++ 庫類遵循 RAII:std::string、std::vector、std::jthread(C++20 起) 以及許多其他類都在建構函式中獲取資源(並在出錯時丟擲異常),在解構函式中釋放資源(從不丟擲異常),並且不需要顯式清理。
此外,標準庫提供了幾個 RAII 包裝器來管理使用者提供的資源:
|
(C++11 起) |
[編輯] 注意
RAII 不適用於管理那些在使用前未獲取的資源:CPU 時間、核心可用性、快取容量、熵池容量、網路頻寬、電力消耗、棧記憶體。對於此類資源,C++ 類建構函式無法保證在物件生命週期內資源的可用性,必須使用其他資源管理方法。