天天看點

c++ 11 智能指針 源碼分析補充知識點(其實可以直接看我下一篇更友善了解:點選打開連結):

原文位址:點選打開連結

簡斷截說:c++的入門坑點大家都是有目共睹的,無非就是指針的了解不深導緻一些野指針,記憶體洩露等問題,是以就不贅述。智能指針正好能夠彌補這些問題,因為它本質是存放在棧的模闆對象,隻是在棧内部包了一層指針。而棧在其生命周期結束時,其中的指針指向的堆記憶體也自然被釋放了。因而實作了智能管理的效果,不需要考慮記憶體問題了,其實有點類似某種單例寫法,程式運作結束,也不用考慮單例對象記憶體問題。

     本次讨論:c++11之前的auto_ptr; c++11新加的unique_ptr, shared_ptr以及weak_ptr。

     頭檔案:#include <memory>

1.auto_ptr

      auto_ptr是我第一個看的智能指針,也是标準庫裡的智能指針,有許多缺陷。

首先看下結構:

c++ 11 智能指針 源碼分析補充知識點(其實可以直接看我下一篇更友善了解:點選打開連結):

從圖中可以看書也是一個模闆,使用方法大緻類似于vector模闆。如下:

  1. class Base1
  2. {
  3. //__int64 ss;
  4. //public:
  5. bool dd;
  6. int m_itest;
  7. public:
  8. virtual void func(){
  9. cout << "test successed" << endl;
  10. }
  11. }; //先寫一個測試類

main函數如下:

  1. int _tmain( int argc, _TCHAR* argv[])
  2. {
  3. auto_ptr <Base1> base1( new Base1); //可了解為先聲明一個名為base1的Base1類型智能指針,然後再base1裡面管理new Base1
  4. if (base1.get()) //get是智能指針的函數,傳回目前目前智能指針對象,即用以判斷是否為空
  5. {
  6. base1->func();
  7. }
  8. return ;
  9. }

正常用法是這樣,然而我們可以再仔細翻看下底層:

c++ 11 智能指針 源碼分析補充知識點(其實可以直接看我下一篇更友善了解:點選打開連結):

從上圖可以看出,該智能指針成員函數也與vector相似,很容易得出

1、base1.get():傳回目前指針對象;

2、base1.release():清空目前智能指針對象,并傳回類型指針。是以假如我們要正常删除,那麼需要這樣:

  1. Base1*base2 = base1.release();
  2. delete base2;

很麻煩對不對,沒關系,還有第三個函數呢

3、base1.reset():從圖中可看出,是重置智能指針,即把記憶體删除,且智能指針指向空,但類型不變,是以可以這樣安全便捷地删除:

base1.reset();
           

然而繼續看,還有一個問題:

c++ 11 智能指針 源碼分析補充知識點(其實可以直接看我下一篇更友善了解:點選打開連結):

auto_ptr還重載了等号操作符,由圖可知意思是把指派智能指針的記憶體交給被指派智能指針,即如下意思:

  1. auto_ptr <Base1> base2;
  2. base2 = base1; //将base1的控制權轉交給base2,且base1清空了
  3. base2->func();

是以這樣就有些問題,控制權可以随便轉換,但是隻有一個在用,用起來會受到諸多限制,是以有了下面的智能指針。

介紹之前先上一張别人的表格,來源:http://my.oschina.net/hevakelcj/blog/465978,這是c++11中的智能指針與boost庫中的比較,原本boost就是為完善auto_ptr搞得這些,現在c++11有了,也就不需要再用咯。

c++ 11 智能指針 源碼分析補充知識點(其實可以直接看我下一篇更友善了解:點選打開連結):

2.unique_ptr

 C++11引入了許多便捷的功能,其中也包括這個,在用之前我們可以先看下底層:

c++ 11 智能指針 源碼分析補充知識點(其實可以直接看我下一篇更友善了解:點選打開連結):

可以清楚的看到,unique_ptr中的拷貝構造和指派操作符delete了,是以也就意味着,他和auto_ptr有差別,控制權唯一,不能随意轉換。用法都差不多:

  1. unique_ptr<Base1> base1( new Base1);
  2. unique_ptr<Base1> base2; //但是不能用拷貝構造和等号指派把base1指派給base2了

但是如果想切換控制權的話也不是沒有辦法,我們可以看到還有個這樣的函數:

c++ 11 智能指針 源碼分析補充知識點(其實可以直接看我下一篇更友善了解:點選打開連結):
c++ 11 智能指針 源碼分析補充知識點(其實可以直接看我下一篇更友善了解:點選打開連結):

要了解這兩個函數,首先要了解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&),并且右值清空。

那麼用法來了:

  1. unique_ptr<Base1> base1( new Base1);
  2. unique_ptr<Base1> base2=move(base1); //base1變成empty
  3. unique_ptr<Base1> base3;
  4. base3 = move(base2); //base2變成empty

其它的成員函數就不一一贅述,和auto_ptr大緻上是相同的。總結,某種程度來說比auto_ptr更為安全,适用部分特殊情況。

3.shared_ptr

如果完全了解了上面兩個ptr的底層,那麼shared_ptr的也就容易了解多了。但是和前兩者有很大差別——

前兩者控制權唯一,切換的時候把前面的清除。而shared_ptr不會,照例看下底層:

c++ 11 智能指針 源碼分析補充知識點(其實可以直接看我下一篇更友善了解:點選打開連結):
c++ 11 智能指針 源碼分析補充知識點(其實可以直接看我下一篇更友善了解:點選打開連結):

很顯然,可以直接指派和調用拷貝構造函數,且不會清空原本的智能指針。用法就很簡單了:

  1. shared_ptr<Base1> base1( new Base1);
  2. shared_ptr<Base1> base2=base1;
  3. shared_ptr<Base1> base3;
  4. base3 = base2; //三個共享一個

有個地方需要注意,當删除一個智能指針時,并不影響其它兩個智能指針的繼續使用。因為該片記憶體添加了一個引用計數,每shared_ptr一次,引用計數+1;每次調用析構函數,引用計數減一。直到最後一個智能指針删除,才會釋放記憶體。

注意:

1、在繼續檢視時,你會發現以下兩個函數:

c++ 11 智能指針 源碼分析補充知識點(其實可以直接看我下一篇更友善了解:點選打開連結):
c++ 11 智能指針 源碼分析補充知識點(其實可以直接看我下一篇更友善了解:點選打開連結):

其實就是和unique_ptr一樣可以通過move來切換控制權,這個時候是切換,不是共享了。

2、接下來繼續翻看,還有兩個函數:

c++ 11 智能指針 源碼分析補充知識點(其實可以直接看我下一篇更友善了解:點選打開連結):

(其實auto_ptr也有,隻是一樣,沒必要截圖了)也就是說,auto_ptr和unique_ptr都可以通過move函數轉換成shared_ptr類型,當然,一樣是切換控制權的形式,即舊的置空。

用法如下:

  1. auto_ptr<Base1> base1( new Base1);
  2. 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智能指針:

c++ 11 智能指針 源碼分析補充知識點(其實可以直接看我下一篇更友善了解:點選打開連結):

也就是讓weak_ptr觀測shared_ptr智能指針,并且在需要時候通過lock函數傳回一個shared_ptr。

6、此外,百科上說:助手類enable_shared_from_this的Shared_from_this會傳回this的shared_ptr,是以隻需讓要被shared_ptr管理的類繼承它即可。我倒是沒試,有興趣的可以試試,大緻意思也就是這般。

繼續閱讀