在C++中,動態內存的管理是由程序員本身申請和釋放的,用一對運算符完成:new和delete。html
new:在動態內存中爲對象分配一塊空間並返回一個指向該對象的指針;程序員
delete:指向一個動態獨享的指針,銷燬對象,並釋放與之關聯的內存。算法
使用堆內存是很是頻繁的操做,容易形成堆內存泄露、二次釋放等問題,爲了更加容易和更加安全的使用動態內存,C++11中引入了智能指針的概念,方便管理堆內存,使得自動、異常安全的對象生存期管理可行。智能指針主要思想是RAII思想,「使用對象管理資源」,在類的構造函數中獲取資源,在類的析構函數中釋放資源。智能指針的行爲相似常規指針,重要的區別是它負責自動釋放所指向的對象。數組
RAII是Resource Acquisition Is Initialization的簡稱,即資源獲取就是初始化:安全
1.定義一個類來封裝資源的分配與釋放;函數
2.構造函數中完成資源的分配及初始化;大數據
3.析構函數中完成資源的清理,能夠保證資源的正確初始化和釋放;ui
4.若是對象是用聲明的方式在棧上建立局部對象,那麼RAII機制就會正常工做,當離開做用域對象會自動銷燬而調用析構函數釋放資源。this
智能指針在C++11版本以後提供,包含在頭文件<memory>中,標準命名std空間下,有auto_ptr、shared_ptr、weak_ptr、unique_ptr四種,其中auto_ptr已被棄用。人工智能
auto_ptr:擁有嚴格對象全部權語義的智能指針;
shared_ptr:擁有共享對象全部權語義的智能指針;
weak_ptr:到 shared_ptr 所管理對象的弱引用;
unique_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 };
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 };
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 };
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.不能把一個原生指針交給兩個智能指針對象管理。
參考:
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++,對大數據、人工智能領域頗感興趣,請多多賜教!
本文爲做者原創,版權歸做者和博客園共有,轉載或引用請註明出處,謝謝!