基礎介紹
RAII (Resource Acquisition Is Initialization) 是一種 C++ 編程范式,這不是一個語法特性,而是一種處理方式。RAII的思想:
- 資源獲取與對象初始化同時發生
- 資源釋放與對象銷毀同時發生
- 通過對象的生命周期來管理資源,確保資源的安全使用
怎么理解RAII呢?前面說了這不是一種庫特性,更像是一種約定,一種管理資源的方式,c++標準庫某些庫特性提供了RAII資源給管理的特性,用戶自己設計的類型也可以按照RAII的思想進行設計。
RAII特點
自動資源管理
這是一個核心的特點,核心就是資源管理,要確保資源的獲取和銷毀與對象的生命周期一致。通過這個特點可以實現資源的自動清理,防止資源泄露。請看下面的例子:
class FileHandler
{private:FILE* file;public: //構造函數獲取資源FileHandler(char* filename){ file = fopen(filename, "r");if(!file) throw std::runtime_error("failed to open file");}//析構函數釋放資源~FileHandler(){if(file)fclose(file);}};
異常安全
void processFile() {FileHandler fh("data.txt"); // 獲取資源// 如果這里拋出異常,FileHandler的析構函數仍會被調用// 確保文件被正確關閉doSomething();
} // 作用域結束,自動調用析構函數釋放資源
常見的RAII應用場景
智能指針
智能指針std::unique_ptr<T>在實現中就采用了RAII的變成范式,當創建這個指針被構造函數構造時,就會獲取資源,當std::unique_ptr<T>變量聲明周期結束時,就會自動釋放該指針對應的對象。注意std::shared_ptr<T>變量不時RAII的思想,這種類型的變量是需要根據引用計數的數量來決定資源是否釋放。示例如下所示:
class Resource {
public:void doWork() { /* ... */ }
};void foo() {std::unique_ptr<Resource> ptr(new Resource()); // RAII管理動態內存ptr->doWork();// 不需要手動刪除,unique_ptr析構時會自動刪除
}
互斥鎖的管理
class Lock
{private:std::mutex& mtx;public:Lock(std::mutex& m):mtx(m){mtx.lock(); //構造時加鎖}~Lock(){mtx.unlock(); //析構時解鎖}
};void funtion()
{std::mutex mtx;Lock lock(mtx); //此處加鎖.....
}//函數結束自定解鎖
數據庫連接
class DBConnection {
private:Connection* conn;
public:DBConnection(const std::string& connectionString) {conn = DatabaseConnect(connectionString);if (!conn) throw std::runtime_error("Connection failed");}~DBConnection() {if (conn) {DatabaseDisconnect(conn);}}
};
優秀實踐
不要使用裸指針
這里不使用裸指針的意思是,如果使用裸指針就需要自己管理這個裸指針的釋放,如有可能盡可能使用一些智能指針,比如std::unique_ptr<T>,也可以使用std::shared_ptr<T>。請看下面的 例子:
Resource* source = new Resource();
....... //業務邏輯
delete source; //手動釋放資源std::unique_ptr<Resource> res = std::make_unique<Resource>();//生命周期結束自動結束
使用標準的RAII工具
- 智能指針:std::unique_ptr std::shared_ptr std::weak_ptr
- 互斥鎖和線程同步:std::lock_guard<T> std::unique_lock std::scoped_lock std::shared_lock
- 標準容器:所有的標準容器都是RAII的,如set map vecotor等