C++編程語言是一款功能強大的編程語言,它能夠幫助開發人員輕松的實現各種功能。其中對於資源的管理是一個比較基礎的知識,值得我們關注。在這裡我們就為大家詳細講解C++資源管理的相關內容。
我最喜歡的對資源的定義是:"任何在你的程序中獲得並在此後釋放的東西。"內存是一個相當明顯的資源的例子。它需要用new來獲得,用delete來釋放。同時也有許多其它類型的資源文件句柄、重要的片斷、Windows中的GDI資源,等等。 將資源的概念推廣到程序中創建、釋放的所有對象也是十分方便的,無論對象是在堆中分配的還是在棧中或者是在全局作用於內生命的。
對於給定的資源的擁有著,是負責釋放資源的一個對象或者是一段代碼。所有權分立為兩種級別--自動的和顯式的automatic and explicit),如果一個對象的釋放是由語言本身的機制來保證的,這個對象的就是被自動地所有。例如,一個嵌入在其他對象中的對象,他的清除需要其他對象來在清除的時候保證。外面的對象被看作嵌入類的所有者。 類似地,每個在棧上創建的對象作為自動變量)的釋放破壞)是在控制流離開了對象被定義的作用域的時候保證的。這種情況下,作用於被看作是對象的所有者。注意所有的自動所有權都是和語言的其他機制相容的,包括異常。無論是如何退出作用域的--正常流程控制退出、一個break語句、一個return、一個goto、或者是一個throw--自動資源都可以被清除。
到目前為止,一切都很好!問題是在引入指針、句柄和抽象的時候產生的。如果通過一個指針訪問一個對象的話,比如對象在堆中分配,C++不自動地關注它的釋放。程序員必須明確的用適當的程序方法來釋放這些資源。比如說,如果一個對象是通過調用new來創建的,它需要用delete來回收。一個文件是用CreateFile(Win32 API)打開的,它需要用CloseHandle來關閉。用EnterCritialSection進入的臨界區Critical Section)需要LeaveCriticalSection退出,等等。一個"裸"指針,文件句柄,或者臨界區狀態沒有所有者來確保它們的最終釋放。基本的資源管理的前提就是確保每個C++資源管理都有他們的所有者。
一個指針,一個句柄,一個臨界區狀態只有在我們將它們封裝入對象的時候才會擁有所有者。這就是我們的第一規則:在構造函數中分配資源,在析構函數中釋放資源。
當你按照規則將所有資源封裝的時候,你可以保證你的程序中沒有任何的資源洩露。這點在當封裝對象Encapsulating Object)在棧中建立或者嵌入在其他的對象中的時候非常明顯。但是對那些動態申請的對象呢?不要急!任何動態申請的東西都被看作一種資源,並且要按照上面提到的方法進行封裝。這一對象封裝對象的鏈不得不在某個地方終止。它最終終止在最高級的所有者,自動的或者是靜態的。這些分別是對離開作用域或者程序時釋放資源的保證。
下面是C++資源管理的一個經典例子。在一個多線程的應用程序中,線程之間共享對象的問題是通過用這樣一個對象聯系臨界區來解決的。每一個需要訪問共享資源的客戶需要獲得臨界區。例如,這可能是Win32下臨界區的實現方法。
- class CritSect
- {
- friend class Lock;
- public:
- CritSect () { InitializeCriticalSection (&_critSection); }
- ~CritSect () { DeleteCriticalSection (&_critSection); }
- private
- void Acquire ()
- {
- EnterCriticalSection (&_critSection);
- }
- void Release ()
- {
- LeaveCriticalSection (&_critSection);
- }
- CRITICAL_SECTION _critSection;
- };
這裡聰明的部分是我們確保每一個進入臨界區的客戶最後都可以離開。"進入"臨界區的狀態是一種資源,並應當被封裝。封裝器通常被稱作一個鎖lock)。
- class Lock
- {
- public:
- Lock (CritSect& critSect) : _critSect (critSect)
- {
- _critSect.Acquire ();
- }
- ~Lock ()
- {
- _critSect.Release ();
- }
- private
- CritSect & _critSect;
- };
鎖一般的用法如下:
- void Shared::Act () throw (char *)
- {
- Lock lock (_critSect);
- // perform action -- may throw
- // automatic destructor of lock
- }
注意無論發生什麼,臨界區都會借助於語言的機制保證釋放。
還有一件需要記住的事情--每一種C++資源管理都需要被分別封裝。這是因為資源分配是一個非常容易出錯的操作,是要資源是有限提供的。我們會假設一個失敗的資源分配會導致一個異常--事實上,這會經常的發生。所以如果你想試圖用一個石頭打兩只鳥的話,或者在一個構造函數中申請兩種形式的資源,你可能就會陷入麻煩。只要想想在一種資源分配成功但另一種失敗拋出異常時會發生什麼。因為構造函數還沒有全部完成,析構函數不可能被調用,第一種資源就會發生洩露。
這種情況可以非常簡單的避免。無論何時你有一個需要兩種以上資源的類時,寫兩個笑的封裝器將它們嵌入你的類中。每一個嵌入的構造都可以保證刪除,即使包裝類沒有構造完成。