程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 條款13:以對象管理資源

條款13:以對象管理資源

編輯:C++入門知識

所謂資源就是,一旦使用了它,將來必須還給系統。C++最常使用的資源就是動態分配內存(如果分配了內存卻不釋放,會導致內存洩露),但內存只是必須要管理的眾多資源之一。其他常見的資源還包括文件描述器(file descriptors)、互斥鎖(mutex locks)、圖形界面中的字型和筆刷、數據庫連接、以及網絡sockets。不論哪一種資源,重要的是,當不再使用它時,必須將它還給系統。

假設我們使用一個用來模擬投資行為(例如股票、債券等)的程序庫,其中各式各樣的投資類型繼承自一個root class Investment:

class Investment
{
 ...     //"投資類型" 繼承體系中的root class
};

進一步假設,這個程序庫系通過一個工廠函數供應我們某特定的Investment對象:

Investment* createInvestment();   //返回指針,指向Investment繼承體系內的動態分配對象。調用者有責任刪除它。
                    //這裡為了簡化,不寫參數

為確保createInvestment返回的資源總是被釋放,把資源放進對象內,我們便可依賴C++的“析構函數自動調用機制”確保資源被釋放。

許多資源被動態分配在heap堆內而後被用於單一區塊或函數內。它們應該在控制流離開那個區塊或函數時被釋放。下面示范如何使用auto_ptr以避免f函數潛在的資源洩露可能性:

<investment> pInv(createInvestment());     ...                              //一如既往地使用pInv
                                //經由auto_ptr的析構函數自動刪除pInv }

這個簡單的例子示范了“以對象管理資源”的兩個關鍵想法:

1、以上代碼中的createInvestment返回的資源被當做其管理者auto_ptr的初值。實際上“以對象管理資源”的觀念常被成為“資源取得時機便是初始化時機”(Resource Acquisition Is Initialization;RAII),因為我們幾乎總是在獲得一筆資源後於同一語句內以它初始化某個管理對象。

管理對象(managing object)運用析構函數確保資源被釋放。不論控制流如何離開區塊,一旦對象被銷毀(例如當對象離開作用域)其析構函數自然會被自動調用於是資源被釋放。

注意:由於auto_ptr被銷毀時會自動刪除它所指之物,所以一定要注意別讓多個auto_ptr同時指向同一個對象。否則若對象被刪除一次以上,會出現“未定義行為”的錯誤,為了預防這個問題,

std::auto_ptr<Investment> pInv1(CreateInvestment());   //pInv1指向CreateInvestment返回物
std::auto_ptr<investment> pInv2(pInv1); //現在pInv2指向對象,pInv1被設為null
pInv1 = pInv2;                        //現在pInv1指向對象,pInv2被設為null

auto_ptr的替代方案是“引用計數型智慧指針”(reference-counting smart pointer; RCSP)。所謂RCSP也是個智能指針,持續追蹤共有多少對象指向某筆資源,並在無人指向它時自動刪除該資源。RCSP提供的行為類型垃圾回收,不同的是RCSP無法打破環狀引用(cycles of reference,例如兩個其實已經沒有被使用的對象彼此互指,因而好像還處在“被使用”狀態)。

TR1的tr1::shared_ptr(見條款54)就是個RCSP,所以可以這樣寫f:

void Func()
{
   ...
   std::tr1::shared_ptr<Investment> pInv1(createInvestment());  //pInv1指向crateInvestment返回物

   std::tr1::shared_ptr<Investment> pInv2(pInv1);         //pInv1和pInv2指向同一個對象

   pInv1 = pInv2;                           //同上,無任何改變
  ...
} //函數結束後,pInv1和pInv2被銷毀,它們所指的對象也被自動銷毀

因為auto_ptr並不是完美無缺的,它的確很方便,但也有缺陷,在使用時要注意避免。首先,不要將auto_ptr對象作為STL容器的元素。C++標准明確禁止這樣做,否則可能會碰到不可預見的結果。

auto_ptr和tr1::shared_ptr兩者都在其析構函數內做delete而不是delete[]動作。故不能講動態分配的數組上使用auto_ptr或tr1::shared_ptr。例如:

std::auto_ptr<std::string> aps(new std::string[10]);	//錯誤!會用上錯誤的delete形式

std::tr1::shared_ptr<int> spi(new int[1024]); //錯誤!會用上錯誤的delete形式

trl1::shared_ptr通常是較佳選擇,因為其copy行為比較直觀。若選擇auto_ptr,復制動作會使被復制物指向null。trl1::shared_ptr在頭文件<memory>中

然後收集了關於auto_ptr的幾種注意事項:
1、auto_ptr不能共享所有權。
2、auto_ptr不能指向數組
3、auto_ptr不能作為容器的成員。
4、不能通過賦值操作來初始化auto_ptr
std::auto_ptr<int> p(new int(42));     //OK
std::auto_ptr<int> p = new int(42);    //ERROR
這是因為auto_ptr 的構造函數被定義為了explicit
5、不要把auto_ptr放入容器

然後筆者從而推薦的是boost的shared_ptr,然後看完shared_ptr關於智能指針的介紹與例子。
5種針對auto_ptr不足的指針如下:需要詳細了解可以去查看相當文檔,與測試新代碼。

scoped_ptr <boost/scoped_ptr.hpp> 簡單的單一對象的唯一所有權。不可拷貝。 scoped_array <boost/scoped_array.hpp> 簡單的數組的唯一所有權。不可拷貝。 shared_ptr <boost/shared_ptr.hpp> 在多個指針間共享的對象所有權。 shared_array <boost/shared_array.hpp> 在多個指針間共享的數組所有權。 weak_ptr <boost/weak_ptr.hpp> 一個屬於 shared_ptr 的對象的無所有權的觀察者。 intrusive_ptr <boost/intrusive_ptr.hpp> 帶有一個侵入式引用計數的對象的共享所有權。


1. shared_ptr是Boost庫所提供的一個智能指針的實現,shared_ptr就是為了解決auto_ptr在對象所有權上的局限性(auto_ptr是獨占的),在使用引用計數的機制上提供了可以共享所有權的智能指針。
2. shared_ptr比auto_ptr更安全
3. shared_ptr是可以拷貝和賦值的,拷貝行為也是等價的,並且可以被比較,這意味這它可被放入標准庫的一般容器(vector,list)和關聯容器中(map)。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved