所謂資源就是,一旦使用了它,未來必須還給系統。C++最常使用的資源就是動態分配內存(若是分配了內存卻不釋放,會致使內存泄露),但內存只是必需要管理的衆多資源之一。其餘常見的資源還包括文件描述器(file descriptors)、互斥鎖(mutex locks)、圖形界面中的字型和筆刷、數據庫鏈接、以及網絡sockets。不論哪種資源,重要的是,當再也不使用它時,必須將它還給系統。html
假設咱們使用一個用來模擬投資行爲(例如股票、債券等)的程序庫,其中各式各樣的投資類型繼承自一個root class Investment:數據庫
class Investment { ... //"投資類型" 繼承體系中的root class };
進一步假設,這個程序庫系經過一個工廠函數供應咱們某特定的Investment對象:數組
Investment* createInvestment(); //返回指針,指向Investment繼承體系內的動態分配對象。調用者有責任刪除它。
//這裏爲了簡化,不寫參數
爲確保createInvestment返回的資源老是被釋放,把資源放進對象內,咱們即可依賴C++的「析構函數自動調用機制」確保資源被釋放。安全
許多資源被動態分配在heap堆內然後被用於單一區塊或函數內。它們應該在控制流離開那個區塊或函數時被釋放。標準程序庫提供的auto_ptr正是針對這種形式而設計的特製產品。auto_ptr是個「類指針(pointer-like)對象」,也就是所謂的「智能指針」,其析構函數自動對其所指對象調用delete。下面示範如何使用auto_ptr以免f函數潛在的資源泄露可能性:網絡
void f() { std::auto_ptr<investment> pInv(createInvestment()); //調用工廠函
... //一如既往地使用pInv
//經由auto_ptr的析構函數自動刪除pInv }
這個簡單的例子示範了「以對象管理資源」的兩個關鍵想法:socket
一、得到資源或馬上放進管理對象(managing object)內。以上代碼中的createInvestment返回的資源被當作其管理者auto_ptr的初值。實際上「以對象管理資源」的觀念常被成爲「資源取得時機即是初始化時機」(Resource Acquisition Is Initialization;RAII),由於咱們幾乎老是在得到一筆資源後於同一語句內以它初始化某個管理對象。函數
二、管理對象(managing object)運用析構函數確保資源被釋放。不論控制流如何離開區塊,一旦對象被銷燬(例如當對象離開做用域)其析構函數天然會被自動調用因而資源被釋放。測試
注意:因爲auto_ptr被銷燬時會自動刪除它所指之物,因此必定要注意別讓多個auto_ptr同時指向同一個對象。不然若對象被刪除一次以上,會出現「未定義行爲」的錯誤,爲了預防這個問題,auto_ptr有個一特殊性質:若經過copy構造函數或copy assignment操做符複製它們,它們會變成null,而複製所得的指針將取得資源的惟一擁有權!ui
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,例如兩個其實已經沒有被使用的對象彼此互指,於是好像還處在「被使用」狀態)。spa
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形式
請牢記:
一、爲防止內存泄露,請使用RAII對象,它們在構造函數中得到資源並在析構函數中釋放資源。
二、兩個常被使用RAII class分別是trl1::shared_ptr和auto_ptr。trl1::shared_ptr一般是較佳選擇,由於其copy行爲比較直觀。若選擇auto_ptr,複製動做會使被複制物指向null。trl1::shared_ptr在頭文件<memory>中
而後收集了關於auto_ptr的幾種注意事項:
一、auto_ptr不能共享全部權。
二、auto_ptr不能指向數組
三、auto_ptr不能做爲容器的成員。
四、不能經過賦值操做來初始化auto_ptr
std::auto_ptr<int> p(new int(42)); //OK
std::auto_ptr<int> p = new int(42); //ERROR
這是由於auto_ptr 的構造函數被定義爲了explicit
五、不要把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)。