標籤: raiic++fpfileinitializationclassc++
C++中的RAII全稱是「Resource acquisition is initialization」,直譯爲「資源獲取就是初始化」。可是這翻譯並無顯示出這個慣用法的真正內涵。RAII的好處在於它提供了一種資源自動管理的方式,當產生異常、回滾等現象時,RAII能夠正確地釋放掉資源。函數
舉個常見的例子:ui
[cpp] view plain copy翻譯
void Func() { FILE *fp; char* filename = "test.txt"; if((fp=fopen(filename,"r"))==NULL) { printf("not open"); exit(0); } ... // 若是 在使用fp指針時產生異常 並退出 // 那麼 fp文件就沒有正常關閉 fclose(fp); }
在資源的獲取到釋放之間,咱們每每須要使用資源,但經常一些不可預計的異常是在使用過程當中產生,就會使資源的釋放環節沒有獲得執行。指針
此時,就可讓RAII慣用法大顯身手了。code
RAII的實現原理很簡單,利用stack上的臨時對象生命期是程序自動管理的這一特色,將咱們的資源釋放操做封裝在一個臨時對象中。對象
具體示例代碼以下:繼承
[cpp] view plain copyci
class Resource{}; class RAII{ public: RAII(Resource* aResource):r_(aResource){} //獲取資源 ~RAII() {delete r_;} //釋放資源 Resource* get() {return r_ ;} //訪問資源 private: Resource* r_; };
好比文件操做的例子,咱們的RAII臨時對象類就能夠寫成:資源
[cpp] view plain copy
class FileRAII{ public: FileRAII(FILE* aFile):file_(aFile){} ~FileRAII() { fclose(file_); }//在析構函數中進行文件關閉 FILE* get() {return file_;} private: FILE* file_; };
則上面這個打開文件的例子就能夠用RAII改寫爲:
[cpp] view plain copy
void Func() { FILE *fp; char* filename = "test.txt"; if((fp=fopen(filename,"r"))==NULL) { printf("not open"); exit(0); } FileRAII fileRAII(fp); ... // 若是 在使用fp指針時產生異常 並退出 // 那麼 fileRAII在棧展開過程當中會被自動釋放,析構函數也就會自動地將fp關閉 // 即便全部代碼是都正確執行了,也無需手動釋放fp,fileRAII它的生命期在此結束時,它的析構函數會自動執行! }
這就是RAII的魅力,它免除了對須要謹慎使用資源時而產生的大量維護代碼。在保證資源正確處理的狀況下,還使得代碼的可讀性也提升了很多。
建立本身的RAII類
通常狀況下,RAII臨時對象不容許複製和賦值,固然更不容許在heap上建立,因此先寫下一個RAII的base類,使子類私有繼承Base類來禁用這些操做:
[cpp] view plain copy
class RAIIBase { public: RAIIBase(){} ~RAIIBase(){}//因爲不能使用該類的指針,定義虛函數是徹底沒有必要的 RAIIBase (const RAIIBase &); RAIIBase & operator = (const RAIIBase &); void * operator new(size_t size); // 不定義任何成員 };
當咱們要寫本身的RAII類時就能夠直接繼承該類的實現:
[cpp] view plain copy
template<typename T> class ResourceHandle: private RAIIBase //私有繼承 禁用Base的全部繼承操做 { public: explicit ResourceHandle(T * aResource):r_(aResource){}//獲取資源 ~ResourceHandle() {delete r_;} //釋放資源 T *get() {return r_ ;} //訪問資源 private: T * r_; };
將Handle類作成模板類,這樣就能夠將class類型放入其中。另外, ResourceHandle能夠根據不一樣資源類型的釋放形式來定義不一樣的析構函數。
因爲不能使用該類的指針,因此使用虛函數是沒有意義的。
注:本身寫的RAII類並無通過大量的實踐,可能存在問題,請三思而慎用。這裏只是記錄下本身的實現想法。