今天看陳碩的多執行緒書上提到了c++中raii技術的使用,通過用c11裡面自帶的智慧型指標來完成對資源的控制,但是一直不太清楚具體raii是怎麼樣的,帶著這樣的疑問,特地去看了幾篇部落格,找了乙個簡單的檔案控制代碼開啟關閉raii管理的例項,瞬間就明白了,這裡分享出來。主要從兩個部分,首先是raii技術的介紹,然後是raii技術的簡單例項。
1、raii定義
raii,它是「resource acquisition is initialization」的首字母縮寫。也稱為「資源獲取就是初始化」,是c++等程式語言常用的管理資源、避免記憶體洩露的方法。它保證在任何情況下,使用物件時先構造物件,最後析構物件。
raii的好處在於它提供了一種資源自動管理的方式,當產生異常、回滾等現象時,raii可以正確地釋放掉資源。
當講述c++資源管理時,bjarne這樣寫道:
使用區域性物件管理資源的技術通常稱為「資源獲取就是初始化」。這種通用技術依賴於建構函式和析構函式的性質以及它們與異常處理的互動作用。
2、raii的理解
看到上面raii的介紹,發現這不就是棧的資源**過程中嗎??
沒錯,思想的確是一樣的。
我們知道在函式內部的一些成員是放置在棧空間上的,當函式返回時,這些棧上的區域性變數就會立即釋放空間,於是bjarne stroustrup就想到確保能執行資源釋放**的地方就是在這個程式段(棧)中放置的物件的析構函式了,因為stack winding會保證它們的析構函式都會被執行。raii就利用了棧裡面的變數的這一特點。
作業系統中棧的操作過程
stack winding & unwinding
當程式執行時,每乙個函式(包括資料、暫存器、程式計數器,等等)在呼叫時,都被對映到棧上。這就是 stack winding。
unwinding 是以相反順序把函式從棧上移除的過程。
正常的 stack unwinding 發生在函式返回時;不正常的情況,比如引發異常,呼叫setjmp和longjmp,也會導致 stack unwinding。
可見 stack unwinding 的過程中,區域性物件的析構函式將逐一被呼叫。這也就是 raii 工作的原理,它是由語言和編譯器來保證的。
3、raii做法
raii 的一般做法是這樣的:在物件構造時獲取資源,接著控制對資源的訪問使之在物件的生命週期內始終保持有效,最後在物件析構的時候釋放資源。藉此,我們實際上把管理乙份資源的責任託管給了乙個存放在棧空間上的區域性物件。
這種做法有兩大好處:
(1)不需要顯式地釋放資源。
(2)採用這種方式,物件所需的資源在其生命期內始終保持有效。
那麼給出了raii的做法,問題就是什麼是資源,我們來明確資源的概念,在計算機系統中,資源是數量有限且對系統正常運轉具有一定作用的元素。
比如,記憶體,檔案控制代碼,網路套接字(network sockets),互斥鎖(mutex locks)等等,它們都屬於系統資源。
由於資源的數量不是無限的,有的資源甚至在整個系統中僅有乙份,因此我們在使用資源時必須嚴格遵循的步驟是:
獲取資源總結使用資源
釋放資源
使用raii,需要自己定義資源類,將自己業務的操作資源封裝起來,然後通過這個資源類來完成資源的構造、使用、釋放。這樣讓我們可以放心的去編寫邏輯功能的**,而不用去關心會不會造成記憶體洩漏這樣的問題。
正如上面介紹的raii技術,這裡我們給出乙個簡單的檔案控制代碼開啟關閉raii管理的例項。
乙個簡單的控制代碼檔案開啟關閉的程式:
void
func()
...// 如果 在使用fp指標時產生異常 並退出
// 那麼 fp檔案就沒有正常關閉
fclose
(fp)
;}
在資源的獲取到釋放之間,我們往往需要使用資源,但常常一些不可預計的異常是在使用過程中產生,就會使資源的釋放環節沒有得到執行。
可能我們會在每個分支上進行關閉操作,來保證資源的正常釋放。
file
*fp;
char* filename =
"test.txt";if
((fp=
fopen
(filename,
"r"))==
null)if
(.....
)elseif(
.....)
...fclose
(fp)
;
使用這種方法,每個分支你都要去釋放資源,不僅**會很冗餘,同時可能在分支較多的情況下,你會忘記釋放,這是十分常見的現象。
此時,就可以讓raii慣用法大顯身手了。
使用raii去管理資源的建立釋放
raii的實現原理很簡單,利用stack上的臨時物件生命期是程式自動管理的這一特點,將我們的資源釋放操作封裝在乙個臨時物件中。
例如:
class
resource
;class
raii
//獲取資源
~raii()
//釋放資源
resource*
get(
)//訪問資源
private
: resource* r_;
};
那麼對於上述的檔案控制代碼開啟關閉的例子,我們可以寫成如下的資源類:
class
fileraii
~fileraii()
//在析構函式中進行檔案關閉
file
*get()
private
:file
* file_;
};
則上面這個開啟檔案的例子就可以用raii改寫為:
class
fileraii
~fileraii()
//在析構函式中進行檔案關閉
file
*get()
private
:file
* file_;};
void
func()
fileraii fileraii
(fp)
;...
// 如果 在使用fp指標時產生異常 並退出
// 那麼 fileraii在棧展開過程中會被自動釋放,析構函式也就會自動地將fp關閉
// 即使所有**是都正確執行了,也無需手動釋放fp,fileraii它的生命期在此結束時,它的析構函式會自動執行!
}
這就是raii的魅力,它免除了對需要謹慎使用資源時而產生的大量維護**。在保證資源正確處理的情況下,還使得**的可讀性也提高了不少。
參考部落格:
C 中的RAII機制
前言 在寫c 設計模式 單例模式的時候,在寫到例項銷毀時,設計的gc類是很巧妙的,而這一巧妙的設計就是根據當物件的生命週期結束時會自動呼叫其析構函式的,而這一巧妙的設計也是有專業的名詞的 raii。那以下將圍繞raii,全面的講解raii的相關知識。什麼是raii?raii是resource acq...
C 中的RAII介紹
raii技術被認為是c 中管理資源的最佳方法,進一步引申,使用raii技術也可以實現安全 簡潔的狀態管理,編寫出優雅的異常安全的 raii是c 的發明者bjarne stroustrup提出的概念,raii全稱是 resource acquisition is initialization 直譯過來...
C 中的RAII用法
c 中的raii全稱是 resource acquisition is initialization 直譯為 資源獲取就是初始化 但是這翻譯並沒有顯示出這個慣用法的真正內涵。raii的好處在於它提供了一種資源自動管理的方式,當產生異常 回滾等現象時,raii可以正確地釋放掉資源。舉個常見的例子 cp...