標籤:color guard 釋放 class 之間 標準 異常 initial har
摘要
RAII技術被認為是C++中管理資源的最佳方法,進一步引申,使用RAII技術也可以實現安全、簡潔的狀態管理,編寫出優雅的異常安全的代碼。
資源管理
RAII是C++的發明者Bjarne Stroustrup提出的概念,RAII全稱是“Resource Acquisition is Initialization”,直譯過來是“資源擷取即初始化”,也就是說在建構函式中申請分配資源,在解構函式中釋放資源。因為C++的語言機制保證了,當一個對象建立的時候,自動調用建構函式,當對象超出範圍的時候會自動調用解構函式。所以,在RAII的指導下,我們應該使用類來管理資源,將資源和對象的生命週期綁定。
智能指標(std::shared_ptr和std::unique_ptr)即RAII最具代表的實現,使用智能指標,可以實現自動的記憶體管理,再也不需要擔心忘記delete造成的記憶體流失。毫不誇張的來講,有了智能指標,代碼中幾乎不需要再出現delete了。
記憶體只是資源的一種,在這裡我們討論一下更加廣義的資源管理。比如說檔案的開啟與關閉、windows中控制代碼的擷取與釋放等等。按照常規的RAII技術需要寫一堆管理它們的類,有的時候顯得比較麻煩。但是如果手動釋放,通常還要考慮各種異常處理,比如說:
void function(){ FILE *f = fopen("test.txt", ‘r‘); if (.....) { fclose(f); return; } else if(.....) { fclose(f); return; } fclose(f); ......}
這裡介紹一個網上實現的我認為比較簡潔的方法,文章在這裡。作者使用了C++11標準中的lambda運算式和std::function相結合的方法,非常簡潔、明了。直接看代碼吧:
#define SCOPEGUARD_LINENAME_CAT(name, line) name##line#define SCOPEGUARD_LINENAME(name, line) SCOPEGUARD_LINENAME_CAT(name, line)#define ON_SCOPE_EXIT(callback) ScopeGuard SCOPEGUARD_LINENAME(EXIT, __LINE__)(callback)class ScopeGuard{public: explicit ScopeGuard(std::function<void()> f) : handle_exit_scope_(f){}; ~ScopeGuard(){ handle_exit_scope_(); }private: std::function<void()> handle_exit_scope_;};int main(){ { A *a = new A(); ON_SCOPE_EXIT([&] {delete a; }); ...... } { std::ofstream f("test.txt"); ON_SCOPE_EXIT([&] {f.close(); }); ...... } system("pause"); return 0;}
作者為了使用方便,還定義了根據行號來對ScopeGuard類型對象命名的宏定義。看到了吧,當ScopeGuard對象超出範圍,ScopeGuard的解構函式中會調用handle_exit_scope_函數,也就是lambda運算式中的內容,所以在lamabda運算式中填上資源釋放的代碼即可,多麼簡潔、明了。既不需要為每種資源管理單獨寫對應的管理類,也不需要考慮手動釋放出現各種異常情況下的處理,同時資源的申請和釋放放在一起去寫,永遠不會忘記。
狀態管理
RAII另一個引申的應用是可以實現安全的狀態管理。一個典型的應用就是線上程同步中,使用std::unique_lock或者std::lock_guard對互斥量std:: mutex進行狀態管理。通常我們不會寫出如下的代碼:
std::mutex mutex_;void function(){ mutex_.lock(); ...... ...... mutex_.unlock();}
因為,在互斥量lock和unlock之間的代碼很可能會出現異常,或者有return語句,這樣的話,互斥量就不會正確的unlock,會導致線程的死結。所以正確的方式是使用std::unique_lock或者std::lock_guard對互斥量進行狀態管理:
std::mutex mutex_;void function(){ std::lock_guard<std::mutex> lock(mutex_); ...... ......}
在建立std::lock_guard對象的時候,會對std::mutex對象進行lock,當std::lock_guard對象在超出範圍時,會自動std::mutex對象進行解鎖,這樣的話,就不用擔心代碼異常造成的線程死結。
總結
通過上面的分析可以看出,RAII的核心思想是將資源或者狀態與對象的生命週期綁定,通過C++的語言機制,實現資源和狀態的安全管理。理解和使用RAII能使軟體設計更清晰,代碼更健壯。
參考
1、http://mindhacks.cn/2012/08/27/modern-cpp-practices/
2、http://www.jellythink.com/archives/101
3、http://www.cppblog.com/aaxron/archive/2011/03/22/142475.html
C++中的RAII介紹