C++之RAII(Resource Acquisition Is Initialization)

來源:互聯網
上載者:User

轉載:https://www.jianshu.com/p/b7ffe79498be 什麼是RAII。

RAII是Resource Acquisition Is Initialization(wiki上面翻譯成 “資源擷取就是初始化”)的簡稱,是C++語言的一種管理資源、避免泄漏的慣用法。利用的就是C++構造的對象最終會被銷毀的原則。RAII的做法是使用一個對象,在其構造時擷取對應的資源,在對象生命期內控制對資源的訪問,使之始終保持有效,最後在對象析構的時候,釋放構造時擷取的資源。 為什麼要使用RAII。

上面說到RAII是用來管理資源、避免資源泄漏的方法。那麼,用了這麼久了,也寫了這麼多程式了,口頭上經常會說資源,那麼資源是如何定義的。在電腦系統中,資源是數量有限且對系統正常運行具有一定作用的元素。比如:網路通訊端、互斥鎖、檔案控制代碼和記憶體等等,它們屬於系統資源。由於系統的資源是有限的,就好比自然界的石油,鐵礦一樣,不是取之不盡,用之不竭的,所以,我們在編程使用系統資源時,都必須遵循一個步驟:
1 申請資源;
2 使用資源;
3 釋放資源。
第一步和第二步缺一不可,因為資源必須要申請才能使用的,使用完成以後,必須要釋放,如果不釋放的話,就會造成資源泄漏。

一個最簡單的例子:

#include <iostream> using namespace std; int main() {     int *testArray = new int [10];     // Here, you can use the array     delete [] testArray;     testArray = NULL ;     return 0; }

小結:
但是如果程式很複雜的時候,需要為所有的new 分配的記憶體delete掉,導致極度臃腫,效率下降,更可怕的是,程式的可理解性和可維護性明顯降低了,當操作增多時,處理資源釋放的代碼就會越來越多,越來越亂。如果某一個操作發生了異常而導致釋放資源的語句沒有被調用,怎麼辦。這個時候,RAII機制就可以派上用場了。 如何使用RAII。

當我們在一個函數內部使用局部變數,當退出了這個局部變數的範圍時,這個變數也就別銷毀了;當這個變數是類對象時,這個時候,就會自動調用這個類的解構函式,而這一切都是自動發生的,不要程式員顯示的去調用完成。這個也太好了,RAII就是這樣去完成的。

由於系統的資源不具有自動釋放的功能,而C++中的類具有自動調用解構函式的功能。如果把資源用類進行封裝起來,對資源操作都封裝在類的內部,在解構函式中進行釋放資源。當定義的局部變數的生命結束時,它的解構函式就會自動的被調用,如此,就不用程式員顯示的去調用釋放資源的操作了。

使用RAII 機制的代碼:

#include <iostream> using namespace std; class ArrayOperation { public :     ArrayOperation()     {         m_Array = new int [10];     }     void InitArray()     {         for (int i = 0; i < 10; ++i)         {             *(m_Array + i) = i;         }     }     void ShowArray()     {         for (int i = 0; i <10; ++i)         {             cout<<m_Array[i]<<endl;         }     }     ~ArrayOperation()     {         cout<< "~ArrayOperation is called" <<endl;         if (m_Array != NULL )         {             delete[] m_Array;  // 非常感謝益可達非常犀利的review,詳細可以參加益可達在本文的評論 2014.04.13            m_Array = NULL ;         }     } private :     int *m_Array; }; bool OperationA(); bool OperationB(); int main() {     ArrayOperation arrayOp;     arrayOp.InitArray();     arrayOp.ShowArray();     return 0;}

不使用RAII(沒有使用類的思想)的代碼

#include <iostream> using namespace std; bool OperationA(); bool OperationB(); int main() {     int *testArray = new int [10];     // Here, you can use the array     if (!OperationA())     {         // If the operation A failed, we should delete the memory         delete [] testArray;         testArray = NULL ;         return 0;     }     if (!OperationB())     {         // If the operation A failed, we should delete the memory         delete [] testArray;         testArray = NULL ;         return 0;     }     // All the operation succeed, delete the memory     delete [] testArray;     testArray = NULL ;     return 0; } bool OperationA() {     // Do some operation, if the operate succeed, then return true, else return false     return false ; } bool OperationB() {     // Do some operation, if the operate succeed, then return true, else return false     return true ; }

上面這個例子沒有多大的實際意義,只是為了說明RAII的機制問題。下面說一個具有實際意義的例子:

#include <iostream>#include <windows.h>#include <process.h>using namespace std;CRITICAL_SECTION cs;int gGlobal = 0;class MyLock{public:    MyLock()    {        EnterCriticalSection(&cs);    }    ~MyLock()    {        LeaveCriticalSection(&cs);    }private:    MyLock( const MyLock &);    MyLock operator =(const MyLock &);};void DoComplex(MyLock &lock ) // 非常感謝益可達犀利的review 2014.04.13{}unsigned int __stdcall ThreadFun(PVOID pv) {    MyLock lock;    int *para = (int *) pv;    // I need the lock to do some complex thing    DoComplex(lock);    for (int i = 0; i < 10; ++i)    {        ++gGlobal;        cout<< "Thread " <<*para<<endl;        cout<<gGlobal<<endl;    }    return 0;}int main(){    InitializeCriticalSection(&cs);    int thread1, thread2;    thread1 = 1;    thread2 = 2;    HANDLE handle[2];    handle[0] = ( HANDLE )_beginthreadex(NULL , 0, ThreadFun, ( void *)&thread1, 0, NULL );    handle[1] = ( HANDLE )_beginthreadex(NULL , 0, ThreadFun, ( void *)&thread2, 0, NULL );    WaitForMultipleObjects(2, handle, TRUE , INFINITE );    return 0;}

這個例子可以說是實際項目的一個模型,當多個進程訪問臨界變數時,為了不出現錯誤的情況,需要對臨界變數進行加鎖;上面的例子就是使用的Windows的臨界地區實現的加鎖。

但是,在使用CRITICAL_SECTION時,EnterCriticalSection和LeaveCriticalSection必須成對使用,很多時候,經常會忘了調用LeaveCriticalSection,此時就會發生死結的現象。當我將對CRITICAL_SECTION的訪問封裝到MyLock類中時,之後,我只需要定義一個MyLock變數,而不必手動的去顯示調用LeaveCriticalSection函數。

上述的兩個例子都是RAII機制的應用,理解了上面的例子,就應該能理解了RAII機制的使用了。

作者:sexycoder
連結:https://www.jianshu.com/p/b7ffe79498be
來源:簡書
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.