什么是RAII
Resource Acquisition Is Initialization,資源獲取即初始化,將資源的生命周期與一個對象的生命周期綁定,舉例來說就是,把一些資源封裝在類中,在構造函數請求資源,在析構函數中釋放資源且絕不拋出異常,而一個對象在生命周期結束時會自動調用析構函數,即資源的生命周期與一個對象的生命周期綁定。
RAII的應用
見如下代碼:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
std::mutex mutex; void func() {} void NoRAII() { mutex.lock(); func(); if (xxx) { mutex.unlock(); // 多次需要調用unlock(),還有可能忘記調用unlock導致一直持有鎖 return ; } ... mutex.unlock(); } void RAII() { // 不需要顯式調用unlock std::lock_guard<std::mutex> lock(mutex); func(); if (xxx) { return ; } ... return ; } |
RAII的應用非常多,C++的STL基本都遵循RAII規范,典型的如vector, string, lock_guard, unique_lock, shared_ptr, unique_ptr等,這里不會介紹這些STL的使用,相信大家也都會使用,如果有相關需求可以留言。
RAII的巧用
最近研究了boost中的ScopeExit,發現這是個很高級的特性,利用RAII特性,可以在作用域結束時自動關閉已經打開的資源或做某些清理操作,類似于unique_ptr,但又比unique_ptr方便,不需要自定義delete函數。
舉例: 如果沒有ScopeExit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
void test () { char *test = new char [100]; if (a) { delete [] test; // count 1 return ; } xxx; if (b) { delete [] test; // count 2 return ; } ... delete [] test; // count 3 } |
使用了ScopeExit
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
void test () { char *test = new char [100]; std::ofstream ofs( "test.txt" ); ScopeExit { delete [] test; // 在test函數生命周期結束后自動執行delete[]操作 ofs.close(); // 在生命周期結束后自動關閉文件,這里只是舉個不恰當例子,ofstream自動生命周期結束后就會關閉 }; if (a) { return ; } xxx; if (b) { return ; } ... } |
當然,正常C++代碼不鼓勵使用裸指針,可以使用智能指針來申請資源,這里只是舉個例子,使用ScopeExit也可以用于處理文件資源的關閉等等。
兩者代碼比較后優劣程度顯而易見,不使用ScopeExit需要在return前多次做資源清理操作,而使用了ScopeExit則只需做一次聲明后在作用域結束后會自動進行相關的資源清理操作,方便而且不易出錯。
ScopeExit實現
這里參考boost使用C++11實現了一套ScopeExit機制
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
class ScopeExit { public : ScopeExit() = default ; ScopeExit( const ScopeExit&) = delete ; void operator=( const ScopeExit&) = delete ; ScopeExit(ScopeExit&&) = default ; ScopeExit& operator=(ScopeExit&&) = default ; template < typename F, typename ... Args> ScopeExit(F&& f, Args&&... args) { func_ = std::bind(std::forward<F>(f), std::forward<Args>(args)...); } ~ScopeExit() { if (func_) { func_(); } }; private : std::function< void ()> func_; }; #define _CONCAT(a, b) a##b #define _MAKE_SCOPE_(line) ScopeExit _CONCAT(defer, line) = [&]() #undef SCOPE_GUARD #define SCOPE_GUARD _MAKE_SCOPE_(__LINE__) |
使用方式如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
void test () { char *test = new char [100]; std::ofstream ofs( "test.txt" ); SCOPE_GUARD{ delete [] test; ofs.close(); }; if (a) { return ; } ... if (b) { return ; } ... } |
RAII還有很多有趣的妙用,后續還會介紹,請持續關注。
到此這篇關于巧妙使用RAII中的ScopeExit的文章就介紹到這了,更多相關RAII妙用ScopeExit內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!
原文鏈接:https://mp.weixin.qq.com/s