原文位址:點選打開連結
簡斷截說:c++的入門坑點大家都是有目共睹的,無非就是指針的了解不深導緻一些野指針,記憶體洩露等問題,是以就不贅述。智能指針正好能夠彌補這些問題,因為它本質是存放在棧的模闆對象,隻是在棧内部包了一層指針。而棧在其生命周期結束時,其中的指針指向的堆記憶體也自然被釋放了。因而實作了智能管理的效果,不需要考慮記憶體問題了,其實有點類似某種單例寫法,程式運作結束,也不用考慮單例對象記憶體問題。
本次讨論:c++11之前的auto_ptr; c++11新加的unique_ptr, shared_ptr以及weak_ptr。
頭檔案:#include <memory>
1.auto_ptr
auto_ptr是我第一個看的智能指針,也是标準庫裡的智能指針,有許多缺陷。
首先看下結構:

從圖中可以看書也是一個模闆,使用方法大緻類似于vector模闆。如下:
- class Base1
- {
- //__int64 ss;
- //public:
- bool dd;
- int m_itest;
- public:
- virtual void func(){
- cout << "test successed" << endl;
- }
- }; //先寫一個測試類
main函數如下:
- int _tmain( int argc, _TCHAR* argv[])
- {
- auto_ptr <Base1> base1( new Base1); //可了解為先聲明一個名為base1的Base1類型智能指針,然後再base1裡面管理new Base1
- if (base1.get()) //get是智能指針的函數,傳回目前目前智能指針對象,即用以判斷是否為空
- {
- base1->func();
- }
- return ;
- }
正常用法是這樣,然而我們可以再仔細翻看下底層:
從上圖可以看出,該智能指針成員函數也與vector相似,很容易得出
1、base1.get():傳回目前指針對象;
2、base1.release():清空目前智能指針對象,并傳回類型指針。是以假如我們要正常删除,那麼需要這樣:
- Base1*base2 = base1.release();
- delete base2;
很麻煩對不對,沒關系,還有第三個函數呢
3、base1.reset():從圖中可看出,是重置智能指針,即把記憶體删除,且智能指針指向空,但類型不變,是以可以這樣安全便捷地删除:
base1.reset();
然而繼續看,還有一個問題:
auto_ptr還重載了等号操作符,由圖可知意思是把指派智能指針的記憶體交給被指派智能指針,即如下意思:
- auto_ptr <Base1> base2;
- base2 = base1; //将base1的控制權轉交給base2,且base1清空了
- base2->func();
是以這樣就有些問題,控制權可以随便轉換,但是隻有一個在用,用起來會受到諸多限制,是以有了下面的智能指針。
介紹之前先上一張别人的表格,來源:http://my.oschina.net/hevakelcj/blog/465978,這是c++11中的智能指針與boost庫中的比較,原本boost就是為完善auto_ptr搞得這些,現在c++11有了,也就不需要再用咯。
2.unique_ptr
C++11引入了許多便捷的功能,其中也包括這個,在用之前我們可以先看下底層:
可以清楚的看到,unique_ptr中的拷貝構造和指派操作符delete了,是以也就意味着,他和auto_ptr有差別,控制權唯一,不能随意轉換。用法都差不多:
- unique_ptr<Base1> base1( new Base1);
- unique_ptr<Base1> base2; //但是不能用拷貝構造和等号指派把base1指派給base2了
但是如果想切換控制權的話也不是沒有辦法,我們可以看到還有個這樣的函數:
要了解這兩個函數,首先要了解c++11引入的move和forward;而要了解move和forward得先了解左值和右值概念。是以還是講全一點吧(已經了解的就直接跳過可以):
補充知識點(其實可以直接看我下一篇更友善了解:點選打開連結):
1、左值與右值:
左值指的是既能夠出現在等号左邊也能出現在等号右邊的變量(或表達式),右值指的則是隻能出現在等号右邊的變量(或表達式)。需要注意的是,左值是指表達式結束後依然存在的持久對象,而右值是指表達式結束時就不再存在的臨時對象。T& 指向的是 lvalue,而 const T& 指向的,卻可能是 lvalue 或 rvalue,左值引用&與右值引用&&(右值引用是c++11加上的)。
2、move和forward:
需要明确的是,move函數可以是用于構造函數,也可以用于指派函數,但都需要手動顯示添加。其實move函數用直白點的話來說就是省去拷貝構造和指派時中間的臨時對象,将資源的記憶體從一個對象移動到(共享也可以)另一個對象。官話是:c++11 中的 move() 是這樣一個函數,它接受一個參數,然後傳回一個該參數對應的右值引用。
std::forward<T>(u) 有兩個參數:T 與 u。當T為左值引用類型時,u将被轉換為T類型的左值,否則u将被轉換為T類型右值。如此定義std::forward是為了在使用右值引用參數的函數模闆中解決參數的完美轉發問題。
其實這裡說的不夠清晰,下次翻譯一篇國外的解釋,閱讀下來就能很好了解move這個概念了,這裡先不深入。
回到這張圖,這兩個函數體也就很明朗了——重載move版本的拷貝構造函數以及重載move版本的等号指派函數。
意思就是:把右值的對象(right)移動給左值(_myt&),并且右值清空。
那麼用法來了:
- unique_ptr<Base1> base1( new Base1);
- unique_ptr<Base1> base2=move(base1); //base1變成empty
- unique_ptr<Base1> base3;
- base3 = move(base2); //base2變成empty
其它的成員函數就不一一贅述,和auto_ptr大緻上是相同的。總結,某種程度來說比auto_ptr更為安全,适用部分特殊情況。
3.shared_ptr
如果完全了解了上面兩個ptr的底層,那麼shared_ptr的也就容易了解多了。但是和前兩者有很大差別——
前兩者控制權唯一,切換的時候把前面的清除。而shared_ptr不會,照例看下底層:
很顯然,可以直接指派和調用拷貝構造函數,且不會清空原本的智能指針。用法就很簡單了:
- shared_ptr<Base1> base1( new Base1);
- shared_ptr<Base1> base2=base1;
- shared_ptr<Base1> base3;
- base3 = base2; //三個共享一個
有個地方需要注意,當删除一個智能指針時,并不影響其它兩個智能指針的繼續使用。因為該片記憶體添加了一個引用計數,每shared_ptr一次,引用計數+1;每次調用析構函數,引用計數減一。直到最後一個智能指針删除,才會釋放記憶體。
注意:
1、在繼續檢視時,你會發現以下兩個函數:
其實就是和unique_ptr一樣可以通過move來切換控制權,這個時候是切換,不是共享了。
2、接下來繼續翻看,還有兩個函數:
(其實auto_ptr也有,隻是一樣,沒必要截圖了)也就是說,auto_ptr和unique_ptr都可以通過move函數轉換成shared_ptr類型,當然,一樣是切換控制權的形式,即舊的置空。
用法如下:
- auto_ptr<Base1> base1( new Base1);
- shared_ptr<Base1> base2=move(base1);
4.weak_ptrred_ptr
weak_ptr更像是shared_ptr的助手:
1、他不像其餘三種,可以通過構造函數直接配置設定對象記憶體;他必須通過shared_ptr來共享記憶體。
2、沒有重載opreator*和->操作符,也就意味着即使配置設定到對象,他也沒法使用該對象
3、不主動參與引用計數,即,share_ptr釋放了,那麼weak_ptr所存的對象也釋放了。
4、使用成員函數use_count()可以檢視目前引用計數,expired()判斷引用計數是否為空。
5、lock()函數,傳回一個shared_ptr智能指針:
也就是讓weak_ptr觀測shared_ptr智能指針,并且在需要時候通過lock函數傳回一個shared_ptr。
6、此外,百科上說:助手類enable_shared_from_this的Shared_from_this會傳回this的shared_ptr,是以隻需讓要被shared_ptr管理的類繼承它即可。我倒是沒試,有興趣的可以試試,大緻意思也就是這般。