C++11智能指針詳解

1、智能指針原由

  在C++中,動態內存的管理是由程序員本身申請和釋放的,用一對運算符完成:new和delete。html

  new:在動態內存中爲對象分配一塊空間並返回一個指向該對象的指針;程序員

  delete:指向一個動態獨享的指針,銷燬對象,並釋放與之關聯的內存。算法

  使用堆內存是很是頻繁的操做,容易形成堆內存泄露、二次釋放等問題,爲了更加容易和更加安全的使用動態內存,C++11中引入了智能指針的概念,方便管理堆內存,使得自動、異常安全的對象生存期管理可行。智能指針主要思想是RAII思想,「使用對象管理資源」,在類的構造函數中獲取資源,在類的析構函數中釋放資源。智能指針的行爲相似常規指針,重要的區別是它負責自動釋放所指向的對象。數組

  RAII是Resource Acquisition Is Initialization的簡稱,即資源獲取就是初始化:安全

  1.定義一個類來封裝資源的分配與釋放;函數

  2.構造函數中完成資源的分配及初始化;大數據

  3.析構函數中完成資源的清理,能夠保證資源的正確初始化和釋放;ui

  4.若是對象是用聲明的方式在棧上建立局部對象,那麼RAII機制就會正常工做,當離開做用域對象會自動銷燬而調用析構函數釋放資源。this

2、智能指針類型

  智能指針在C++11版本以後提供,包含在頭文件<memory>中,標準命名std空間下,有auto_ptr、shared_ptr、weak_ptr、unique_ptr四種,其中auto_ptr已被棄用。人工智能

  auto_ptr:擁有嚴格對象全部權語義的智能指針;

  shared_ptr:擁有共享對象全部權語義的智能指針;

  weak_ptr:到 shared_ptr 所管理對象的弱引用;

  unique_ptr:擁有獨有對象全部權語義的智能指針。

2.1 auto_ptr

  auto_ptr是經過由 new 表達式得到的對象,並在auto_ptr自身被銷燬時刪除該對象的智能指針,它可用於爲動態分配的對象提供異常安全、傳遞動態分配對象的全部權給函數和從函數返回動態分配的對象,是一個輕量級的智能指針,適合用來管理生命週期比較短或者不會被遠距離傳遞的動態對象,最好是侷限於某個函數內部或者是某個類的內部。

  聲明:

  templateclass T class auto_ptr;

  template<> class auto_ptr<void>;  // 對類型void特化  

  成員函數:

  (1) get: 得到內部對象的指針;

  (2) release:釋放被管理對象的全部權,將內部指針置爲空,返回內部對象的指針,此指針須要手動釋放;

  (3) reset:銷燬內部對象並接受新的對象的全部權;

  (4) operator=:從另外一auto_ptr轉移全部權;

  (5) operator*operator->:訪問被管理對象。

  注意事項:

  (1) 其構造函數被聲明爲explicit,所以不能使用賦值運算符對其賦值,即不能使用相似這樣的形式 auto_ptr<int> p = new int;

  (2) auto_ptr 的對象全部權是獨佔性的,使用拷貝構造和賦值操做符時,會形成對象全部權的轉移,被拷貝對象在拷貝過程當中被修改;

  (3) 基於第二條,所以不能將auto_ptr放入到標準容器中或做爲容器的成員;

  (4) auto_ptr不能指向數組,釋放時沒法肯定是數組指針仍是普通指針;

  (5) 不能把一個原生指針交給兩個智能指針對象管理,對其它智能指針也是如此。

  auto_ptr是最先期的智能指針,C++11 中已被棄用,C++17 中移除,建議使用unique_ptr代替auto_ptr。

  簡單實現:

 1 template<class T>
 2 class AutoPointer
 3 {
 4 public:
 5     AutoPointer(T* ptr)
 6       :mPointer(ptr){}
 7 
 8     AutoPointer(AutoPointer<T>& other)
 9     {
10         mPointer= other.mPointer;  //管理權進行轉移
11         other.mPointer= NULL;
12     }
13 
14     AutoPointer& operator = (AutoPointer<T>& other)
15     {
16         if(this != &other)
17         {
18             delete mPointer;
19             mPointer = other.mPointer;  //管理權進行轉移
20             other.mPointer= NULL;
21         }
22 
23         return *this;
24     }
25 
26     ~AutoPointer()
27     {
28         delete mPointer;
29     }
30 
31     T& operator * ()
32     {
33         return *mPointer;
34     }
35 
36     T* operator -> ()
37     {
38         return mPointer;
39     }
40 
41 private:
42 
43     T* mPointer;
44 };

2.2 shared_ptr

  shared_ptr多個指針指向相同的對象,也叫共享指針。shared_ptr採用了引用計數的方式,更好地解決了賦值與拷貝的問題,每個shared_ptr的拷貝都指向相同的內存,每拷貝一次內部的引用計數加1,每析構一次內部的引用計數減1,爲0時自動刪除所指向的堆內存。shared_ptr內部的引用計數是線程安全的,可是對象的讀取時須要加鎖。

聲明:

  templateclass T class shared_ptr; 

  成員函數:

  (1) get: 得到內部對象的指針;

  (2) swap:交換所管理的對象;

  (3) reset:替換所管理的對象;

  (4) use_count:返回shared_ptr所指對象的引用計數;

  (5) operator*operator->:解引用存儲的對象指針;

  (6) operator=:對shared_ptr賦值;

  (7) operator bool:檢查是否有關聯的管理對象;

  (8) owner_before:提供基於擁有者的共享指針排序。

  交換: std::swap(std::shared_ptr) 特化的swap算法用於交換兩個智能指針。

  初始化:經過構造函數傳入指針初始化,也可使用std::make_shared 或 std::allocate_shared 函數初始化。

  注意事項:

  (1) 不能將指針直接賦值給一個智能指針,一個是類,一個是指針。不能使用相似這樣的形式 shared_ptr<int> p = new int;

  (2) 避免循環引用,這是shared_ptr的一個最大陷阱,致使內存泄漏,這一點在weak_ptr中將獲得完善;

  (3) 管理數組指針時,須要制定Deleter以使用delete[]操做符銷燬內存,shared_ptr並無針對數組的特化版本;

  (4) 不能把一個原生指針交給兩個智能指針對象管理,對其它智能指針也是如此。

  簡單實現:

 1 template <typename T>
 2 class SharedPointer
 3 {
 4 private:
 5     
 6     class Implement
 7     {
 8     public:
 9         Implement(T* p) : mPointer(p), mRefs(1){}
10         ~Implement(){ delete mPointer;}
11         
12         T* mPointer;  //實際指針
13         size_t mRefs;  // 引用計數
14     };
15     
16     Implement* mImplPtr;
17     
18 public:
19     
20     explicit SharedPointer(T* p)
21       : mImplPtr(new Implement(p)){}
22         
23     ~SharedPointer()
24     {
25         decrease();  // 計數遞減
26     }
27     
28     SharedPointer(const SharedPointer& other)
29       : mImplPtr(other.mImplPtr)
30     {
31         increase();  // 計數遞增
32     }
33     
34     SharedPointer& operator = (const SharedPointer& other)
35     {
36         if(mImplPtr != other.mImplPtr)  // 避免自賦值
37         {
38             decrease();
39             mImplPtr = other.mImplPtr;
40             increase();
41         }
42         
43         return *this;
44     }
45     
46     T* operator -> () const
47     {
48         return mImplPtr->mPointer;
49     }
50     
51     T& operator * () const
52     {
53         return *(mImplPtr->mPointer);
54     }
55     
56 private:
57     
58     void decrease()
59     {
60         if(--(mImplPtr->mRefs) == 0)
61         {
62             delete mImplPtr;
63         }
64     }
65     
66     void increase()
67     {
68         ++(mImplPtr->mRefs);
69     }
70 };

2.3 weak_ptr

  weak_ptr是爲了配合shared_ptr而引入的一種智能指針,用於專門解決shared_ptr循環引用的問題,由於它不具備普通指針的行爲,沒有重載operator * 和 ->,它的最大做用在於協助shared_ptr工做,像旁觀者那樣觀測資源的使用狀況。weak_ptr能夠從一個shared_ptr或者另外一個weak_ptr對象構造,得到資源的觀測權。但weak_ptr沒有共享資源,它的構造不會引發指針引用計數的增長。weak_ptr可使用一個很是重要的成員函數lock(),從被觀測的shared_ptr得到一個可用的shared_ptr對象,從而操做資源。

  聲明:

  templateclass T class weak_ptr; 

  成員函數:

  (1) swap:交換所管理的對象;

  (2) reset:替換所管理的對象;

  (3) use_count:返回shared_ptr所指對象的引用計數;

  (4) operator=:對shared_ptr賦值;

  (5) expired:檢查被引用的對象是否已刪除;

  (6) owner_before:提供基於擁有者的共享指針排序;

  (7) lock:建立管理被引用的對象的shared_ptr。

  交換:std::swap(std::weak_ptr) 特化的swap算法用於交換兩個智能指針。

  注意事項:

  (1) 不能將指針直接賦值給一個智能指針,一個是類,一個是指針。不能使用相似這樣的形式 shared_ptr<int> p = new int;

  (2) 不能把一個原生指針交給兩個智能指針對象管理,對其它智能指針也是如此。

  簡單實現:weak_ptr的典型實現存儲二個指針,即指向控制塊的指針和做爲構造來源的shared_ptr的存儲指針。

  如下是VC的源碼實現:

 1 template<class _Ty>
 2 class weak_ptr
 3     : public _Ptr_base<_Ty>
 4 {    // class for pointer to reference counted resource
 5     typedef typename _Ptr_base<_Ty>::_Elem _Elem;
 6 
 7 public:
 8     weak_ptr()
 9     {    // construct empty weak_ptr object
10     }
11 
12     template<class _Ty2>
13     weak_ptr(const shared_ptr<_Ty2>& _Other,
14         typename enable_if<is_convertible<_Ty2 *, _Ty *>::value,
15         void *>::type * = 0)
16     {    // construct weak_ptr object for resource owned by _Other
17         this->_Resetw(_Other);
18     }
19 
20     weak_ptr(const weak_ptr& _Other)
21     {    // construct weak_ptr object for resource pointed to by _Other
22         this->_Resetw(_Other);
23     }
24 
25     template<class _Ty2>
26     weak_ptr(const weak_ptr<_Ty2>& _Other,
27         typename enable_if<is_convertible<_Ty2 *, _Ty *>::value,
28         void *>::type * = 0)
29     {    // construct weak_ptr object for resource pointed to by _Other
30         this->_Resetw(_Other);
31     }
32 
33     ~weak_ptr()
34     {    // release resource
35         this->_Decwref();
36     }
37 
38     weak_ptr& operator=(const weak_ptr& _Right)
39     {    // assign from _Right
40         this->_Resetw(_Right);
41         return (*this);
42     }
43 
44     template<class _Ty2>
45     weak_ptr& operator=(const weak_ptr<_Ty2>& _Right)
46     {    // assign from _Right
47         this->_Resetw(_Right);
48         return (*this);
49     }
50 
51     template<class _Ty2>
52     weak_ptr& operator=(shared_ptr<_Ty2>& _Right)
53     {    // assign from _Right
54         this->_Resetw(_Right);
55         return (*this);
56     }
57 
58     void reset()
59     {    // release resource, convert to null weak_ptr object
60         this->_Resetw();
61     }
62 
63     void swap(weak_ptr& _Other)
64     {    // swap pointers
65         this->_Swap(_Other);
66     }
67 
68     bool expired() const
69     {    // return true if resource no longer exists
70         return (this->_Expired());
71     }
72 
73     shared_ptr<_Ty> lock() const
74     {    // convert to shared_ptr
75         return (shared_ptr<_Elem>(*this, false));
76     }
77 };

 

2.4 unique_ptr

  unique_ptr實際上至關於一個安全性加強了的auto_ptr。unique_ptr是經過指針佔有並管理另外一對象,並在unique_ptr離開做用域時釋放該對象的智能指針。unique_ptr的使用標誌着控制權的轉移,同一時刻只能有一個unique_ptr指向給定對象,經過禁止拷貝語義、只有移動語義來實現。相比與原始指針unique_ptr用於其RAII的特性,使得在出現異常的狀況下,動態資源能獲得釋放。

  聲明:

  templateclass T, class Deleter std::default_delete<Tclass unique_ptr;

  templateclass T, class Deleterclass unique_ptr<T[], Deleter>;  // 管理數組指針

  成員函數:

  (1) get: 返回指向被管理對象的指針;

  (2) get_deleter:返回用於析構被管理對象7的刪除器;

  (3) swap:交換所管理的對象;

  (4) reset:替換所管理的對象;

  (5) release:返回一個指向被管理對象的指針,並釋放全部權;

  (6) operator bool:檢查是否有關聯的被管理對象;

  (7) operator=:爲unique_ptr賦值;

  (8) operator*operator->:解引用存儲的對象指針。

  注意事項:

  (1) 不能將指針直接賦值給一個智能指針,一個是類,一個是指針。不能使用相似這樣的形式 shared_ptr<int> p = new int;

  (2) 不能把一個原生指針交給兩個智能指針對象管理,對其它智能指針也是如此。

  簡單實現:

  1 //default deleter for unique_ptr
  2 template<typename T>
  3 struct DefaultDeleter
  4 {
  5     void operator () (T *p)
  6     {
  7         if(p)
  8         {
  9             delete p;
 10             p = NULL;
 11         }
 12     }
 13 };
 14 
 15 template<typename T, typename Deleter = DefaultDeleter<T>>
 16 class unique_ptr
 17 {
 18 public:
 19 
 20     // construct 
 21     unique_ptr(T *pT = NULL);
 22 
 23     // destroy
 24     ~unique_ptr();
 25 
 26 private:
 27 
 28     // not allow copyable
 29     unique_ptr(const unique_ptr &);
 30 
 31     unique_ptr&operator=(const unique_ptr &);
 32 
 33 public:
 34 
 35     // reset 
 36     void reset(T *p);
 37 
 38     // release the own of the pointer
 39     T* release();
 40 
 41     // get the pointer
 42     T* get();
 43 
 44     // convert unique_ptr to bool
 45     operator bool() const;
 46 
 47     // overload for operator *
 48     T& operator * ();
 49 
 50     // overload for operator ->
 51     T* operator -> ();
 52 
 53 private:
 54 
 55     T *m_pT;  //pointer
 56     
 57     Deleter m_deleter;  //deleter
 58     
 59     void del();  //call deleter
 60 };
 61 
 62 
 63 template<typename T, typename Deleter>
 64  unique_ptr<T, Deleter>::unique_ptr(T *pT) :m_pT(pT)
 65 {
 66 
 67 }
 68 
 69 template<typename T, typename Deleter>
 70  unique_ptr<T, Deleter>::~unique_ptr()
 71 {
 72     del();
 73 }
 74 
 75 template<typename T, typename Deleter>
 76  void unique_ptr<T, Deleter>::del()
 77 {
 78     if(*this)
 79     {
 80         m_deleter(m_pT);
 81         m_pT = NULL;
 82     }
 83 }
 84 
 85 template<typename T, typename Deleter>
 86  T* unique_ptr<T, Deleter>::get()
 87 {
 88     return m_pT;
 89 }
 90 
 91 template<typename T, typename Deleter>
 92  void unique_ptr<T, Deleter>::reset(T *p)
 93 {
 94     del();
 95     m_pT = p;
 96 }
 97 
 98 template<typename T, typename Deleter>
 99  T* unique_ptr<T, Deleter>::release()
100 {
101     T *p = m_pT;
102     m_pT = NULL;
103     return p;
104 }
105 
106 template<typename T, typename Deleter>
107  unique_ptr<T, Deleter>::operator bool() const
108 {
109     return NULL != m_pT;
110 }
111 
112 template<typename T, typename Deleter>
113  T& unique_ptr<T, Deleter>::operator * ()
114 { 
115     return *m_pT;
116 }
117 
118 template<typename T, typename Deleter>
119  T* unique_ptr<T, Deleter>::operator -> ()
120 {
121     return m_pT;
122 }

3、總結

  智能指針就是模擬指針動做的類,通常智能指針都會重載 -> 和 * 操做符。智能指針主要做用是管理動態內存的釋放。

  1.不要使用std::auto_ptr;

  2.當你須要一個獨佔資源全部權的指針,且不容許任何外界訪問,請使用std::unique_ptr;

  3.當你須要一個共享資源全部權的指針,請使用std::shared_ptr;

  4.當你須要一個能訪問資源,但不控制其生命週期的指針,請使用std::weak_ptr;

  5.不能把一個原生指針交給兩個智能指針對象管理。

 

參考:

https://zh.cppreference.com/w/cpp/memory

https://blog.csdn.net/zhangye3017/article/details/80429780

做者:KeepHopes
出處:http://www.javashuo.com/article/p-kqmhmutu-gm.html
關於做者:專一C++,對大數據、人工智能領域頗感興趣,請多多賜教!
本文爲做者原創,版權歸做者和博客園共有,轉載或引用請註明出處,謝謝!
相關文章
相關標籤/搜索