一、智能指針起因
在C++中,動態內存的管理是由程序員自己申請和釋放的,用一對運算符完成:new和delete。
new:在動態內存中為對象分配一塊空間並返回一個指向該對象的指針;
delete:指向一個動態獨享的指針,銷毀對象,並釋放與之關聯的內存。
使用堆內存是非常頻繁的操作,容易造成堆內存泄露、二次釋放等問題,為了更加容易和更加安全的使用動態內存,C++11中引入了智能指針的概念,方便管理堆內存,使得自動、異常安全的對象生存期管理可行。智能指針主要思想是RAII思想,“使用對象管理資源”,在類的構造函數中獲取資源,在類的析構函數中釋放資源。智能指針的行為類似常規指針,重要的區別是它負責自動釋放所指向的對象。
RAII是Resource Acquisition Is Initialization的簡稱,即資源獲取就是初始化:
1.定義一個類來封裝資源的分配與釋放;
2.構造函數中完成資源的分配及初始化;
3.析構函數中完成資源的清理,可以保證資源的正確初始化和釋放;
4.如果對象是用聲明的方式在棧上創建局部對象,那么RAII機制就會正常工作,當離開作用域對象會自動銷毀而調用析構函數釋放資源。
二、智能指針類型
智能指針在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自身被銷毀時刪除該對象的智能指針,它可用於為動態分配的對象提供異常安全、傳遞動態分配對象的所有權給函數和從函數返回動態分配的對象,是一個輕量級的智能指針,適合用來管理生命周期比較短或者不會被遠距離傳遞的動態對象,最好是局限於某個函數內部或者是某個類的內部。
聲明:
template< class 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內部的引用計數是線程安全的,但是對象的讀取時需要加鎖。
聲明:
template< class 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對象,從而操作資源。
聲明:
template< class 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的特性,使得在出現異常的情況下,動態資源能得到釋放。
聲明:
template< class T, class Deleter = std::default_delete<T> > class unique_ptr;
template< class T, class Deleter> class 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 }
三、總結
智能指針就是模擬指針動作的類,一般智能指針都會重載 -> 和 * 操作符。智能指針主要作用是管理動態內存的釋放。
1.不要使用std::auto_ptr;
2.當你需要一個獨占資源所有權的指針,且不允許任何外界訪問,請使用std::unique_ptr;
3.當你需要一個共享資源所有權的指針,請使用std::shared_ptr;
4.當你需要一個能訪問資源,但不控制其生命周期的指針,請使用std::weak_ptr;
5.不能把一個原生指針交給兩個智能指針對象管理。

