很多人都誤以爲智能指針是一個指針,其實不然,智能指針不是指針,智能指針是一個模板,由智能指針實例化出來的的對象具有和常規指針相似的行爲,但是他能夠自動的釋放所指向的對象,所以我們稱之爲智能指針。如果我們用普通指針來創建一個指向某個對象的指針,那麼我們最後必須要手動釋放這塊空間,而智能指針它是一個類,它釋放空間是通過析構函數完成的,正是由於有了這一層封裝機制,所以智能指針才能夠管理一個對象的生命週期。(定義一個類來封裝資源的分配和釋放,在構造函數完成資源的分配和初始化,在析構函數完成資源的清理,可以保證資源的正確初始化和釋放。這樣的方法稱爲RAII)
起初在C++標準庫裏面是沒有智能指針的,直到C++11中才加入了shared_ptr和unique_ptr,weak_ptr。最早的智能指針在Boost庫裏面,Boost是爲C++標準庫提供擴展的一些C++程序的總稱,由Boost社區組織開發維護。
1、auto_ptr
auto_ptr在STL中早就存在了,auto_ptr是通過權限轉移的方式來防止值拷貝所帶來問題,所謂權限轉移就是說開闢的動態內存任何時刻只能由一個指針指向。
下面通過實現自己的AutoPtr來剖析一下auto_ptr。現在已經不用aotu_ptr了,常用的是scoped_ptr和shared_ptr.
<span style="font-size:14px;">template<typename T> class AutoPtr //AutoPtr是一個類模板,不是指針類型 { public: AutoPtr(T* ptr=0); AutoPtr(AutoPtr<T>& ap); AutoPtr<T>& operator=(AutoPtr<T>& ap); T* Get(); T* Release(); void Reset(T* ptr=0); T& operator*(); T* operator->(); ~AutoPtr(); private: T* _ptr; }; template<typename T> T* AutoPtr<T>::Get() { return _ptr; } template<typename T> T* AutoPtr<T>::Release() { T* tmp = _ptr; _ptr = NULL; return tmp; } template<typename T> void AutoPtr<T>::Reset(T* ptr = 0) { delete _ptr; _ptr = ptr; ptr = NULL; } template<typename T> AutoPtr<T>::AutoPtr(T* ptr=0) :_ptr(ptr){} template<typename T> AutoPtr<T>::AutoPtr(AutoPtr<T>& ap) //auto_ptr採用權限轉移的方式,確保始終只有一個指針指向這塊空間 { _ptr = ap._ptr; ap._ptr = NULL; //權限轉移 } template<typename T> AutoPtr<T>& AutoPtr<T>::operator=(AutoPtr<T>& ap) { delete _ptr; _ptr = ap._ptr; ap._ptr = NULL; //權限轉移 return *this; } template<typename T> T& AutoPtr<T>::operator*() { return *_ptr; } template<typename T> T* AutoPtr<T>::operator->() //有特殊處理 { return _ptr; } template<typename T> AutoPtr<T>::~AutoPtr() { if (_ptr != NULL) { delete _ptr; _ptr = NULL; } }</span><span style="font-size: 19px;"> </span>
下面我們先來介紹boost庫中的幾種常用的智能指針,由於boost庫不是C++標準庫,所以我們在使用boost中的智能指針之前先要下載一個boost庫,並把它包含到C++標準庫中。
1、scoped_ptr
由於auto_ptr的行爲與真正的指針有很大區別,尤其是權限轉移這種方法。爲了防止值拷貝帶來的問題,所以scoped_ptr從根本上就不允許拷貝和賦值(防賦值、防拷貝)。
template<typename T> class ScopedPtr //防拷貝,防賦值,使得scopedptr看起來更像一個指針類型,但實際上scopedptr是一個類模板 { public: explicit ScopedPtr(T* ptr); T& operator*(); T* operator->(); ~ScopedPtr(); T* Get() const; void Reset(T *p=0); void Swap(ScopedPtr<T>& sp); protected: ScopedPtr(const ScopedPtr<T>& sp); //將拷貝和賦值運算符聲明爲保護,防賦值、防拷貝 ScopedPtr<T>& operator=(const ScopedPtr<T>& ap); private: T* _ptr; }; template<typename T> T* ScopedPtr<T>::Get() const { return _ptr; } template<typename T> void ScopedPtr<T>::Reset(T *p = 0) { delete _ptr; _ptr = p; p = NULL; } template<typename T> void ScopedPtr<T>::Swap(ScopedPtr<T>& sp) { swap(_ptr,sp._ptr); } template<typename T> ScopedPtr<T>::ScopedPtr(T* ptr) :_ptr(ptr) {} template<typename T> T& ScopedPtr<T>::operator*() { return *_ptr; } template<typename T> T* ScopedPtr<T>::operator->() //有特殊處理 { return _ptr; } template<typename T> ScopedPtr<T>::~ScopedPtr() { if (NULL!= _ptr) { delete _ptr; _ptr =NULL; } }
2、scoped_array
scoped_array是用來管理數組的。
<span style="font-size:14px;">template<typename T> class ScopedArray { public: ScopedArray(T* ptr); ~ScopedArray(); T& operator[](size_t index); void Reset(T* ptr=0); T* Get() const; protected: ScopedArray(const ScopedArray<T>&); ScopedArray<T>& operator=(const ScopedArray<T>&); private: T* _ptr; }; template<typename T> void ScopedArray<T>::Reset(T* ptr = 0) { if (_ptr != ptr||_ptr==NULL) { delete[] _ptr; _ptr = ptr; ptr = NULL; } } template<typename T> T* ScopedArray<T>::Get() const { return _ptr; } template<typename T> ScopedArray<T>::ScopedArray(T* ptr) :_ptr(ptr) {} template<typename T> ScopedArray<T>::~ScopedArray() { if (NULL != _ptr) { delete[] _ptr; _ptr = NULL; } } template<typename T> T& ScopedArray<T>::operator[](size_t index) { return _ptr[index]; }</span><span style="font-size: 24px;"> </span>
3、shared_ptr
shared_ptr允許拷貝和賦值,其底層實現是以"引用計數"爲基礎的,通過引用計數來控制空間的釋放,當一塊空間創建時引用計數爲1,當有新的指針指向這塊空間時,引用計數加1,反之減1,直到引用計數減爲0時才真的釋放這塊空間。所以說shared_ptr更像一個指針。
template<typename T> class SharedPtr //採用引用計數,實現一個可以有多個指針指向同一塊內存的類模板,SharedPtr是類模板,不是智能指針類型 { public: SharedPtr(T* ptr); SharedPtr(const SharedPtr<T>& sp); SharedPtr<T>& operator=(SharedPtr<T> sp); T& operator*(); T* operator->(); ~SharedPtr(); int Count() { return *_pCount; } private: void Release() { if (--(*_pCount) == 0) { delete _ptr; delete _pCount; _ptr = NULL; _pCount = NULL; } } private: T* _ptr; int* _pCount; //指向引用計數的空間 }; template<typename T> SharedPtr<T>::SharedPtr(T* ptr) :_ptr(ptr) , _pCount(new int(1)){} template<typename T> SharedPtr<T>::SharedPtr(const SharedPtr<T>& sp) { _ptr = sp._ptr; _pCount= sp._pCount; ++(*_pCount); } template<typename T> SharedPtr<T>& SharedPtr<T>::operator=(SharedPtr<T> sp) { std::swap(sp._ptr,_ptr); std::swap(sp._pCount,_pCount); return *this; } template<typename T> T& SharedPtr<T>::operator*() { return *_ptr; } template<typename T> T* SharedPtr<T>::operator->() { return _ptr; } template<typename T> SharedPtr<T>::~SharedPtr() { Release(); }
4、shared_array
shared_array也是管理數組的。
<span style="font-size:14px;">template<typename T> class SharedArray { public: SharedArray(T* ptr); ~SharedArray(); SharedArray(const SharedArray<T>& sp); SharedArray<T>& operator=(SharedArray<T> sp); T& operator[](size_t index); private: T* _ptr; int* _pCount; }; template<typename T> SharedArray<T>::SharedArray(T* ptr) :_ptr(ptr) , _pCount(new int(1)) {} template<typename T> SharedArray<T>::~SharedArray() { if (--(*_pCount) == 0) { delete[] _ptr; _ptr = NULL; delete _pCount; _pCount = NULL; } } template<typename T> SharedArray<T>::SharedArray(const SharedArray<T>& sp) { _ptr = sp._ptr; _pCount = sp._pCount; ++*pCount; } template<typename T> SharedArray<T>& SharedArray<T>::operator=(SharedArray<T> sp) { swap(_ptr,sp._ptr); swap(_pCount,sp._pCount); return *this; } template<typename T> T& SharedArray<T>::operator[](size_t index) { return _ptr[index]; }</span><span style="font-size: 24px;"> </span>