智能指針就是智能,自動化地管理指針所指向的動態資源的釋放,那仫既然已經有了指針的概念那仫為什仫還會有智能指針呢?請看下面一種場景
void fun() { int *ptr=new int(1); if(1) throw 1; delete ptr; } void test1() { try { fun(); } catch(...) { cout<<"未知異常"<<endl; pre="">
在上述場景中我們發現如果在程序中出現異常進而拋出異常,會出現內存洩漏的問題(ptr無法釋放),此時我們就需要一個能夠出作用域就自動釋放的內存空間的指針,而智能指針的提出就是為了解決異常安全的問題;智能指針其實並不是指針,它一個類,但是它卻做著和指針類似的事兒:在構造函數中完成資源的分配和初始化,在析構函數中完成資源的清理,保證資源的正確初始化和釋放.這也就提出了一個和智能指針類似的概念-RAII(Resource Acquisition Initialization),資源分配及初始化,定義一個類來封裝資源的分配和釋放
auto_ptr的模擬實現
template<typename t=""> class AutoPtr { public: AutoPtr(T *ptr) :_ptr(ptr) { cout<<"AutoPtr()"<<endl; t="">& ap) //權限轉移 { _ptr=ap._ptr; ap._ptr=0; } AutoPtr<t>& operator=(AutoPtr<t>& ap) { if(_ptr != ap._ptr) { delete _ptr; _ptr=ap._ptr; ap._ptr=0; } return *this; } T& operator*() { return *_ptr; } T* operator->() { return _ptr; } void Reset(T *ptr) //重置 { delete _ptr; _ptr=ptr; } ~AutoPtr() { if(_ptr != 0) { cout<<"~AutoPtr()"<<endl; _ptr="0;" bool="" delete="" pre="" private:="">
auto_ptr是通過權限轉移的方法解決多個指針指向同一塊空間而該空間不會被釋放多次的問題的.所謂的權限轉移主要體現在拷貝構造和賦值操作符重載上,權限轉移其實就是當需要以一個已有的對象初始化另一個對象或者需要給有一個對象賦值時我們可以使得維護該對象的智能指針為空,而另一個維護該對象的智能指針指向該空間.
scoped_ptr的模擬實現
templateclass ScopedPtr { public: ScopedPtr(T *ptr) :_ptr(ptr) {} T& operator*() { return *_ptr; } T* operator->() { return _ptr; } ~ScopedPtr() { if(_ptr != 0) { delete _ptr; _ptr=0; } } protected: ScopedPtr(ScopedPtr & sp); ScopedPtr & operator=(ScopedPtr & ap); private: T *_ptr; };
scoped_ptr是一個防拷貝,防賦值的智能指針,所以scoped_ptr的拷貝構造函數和賦值操作符重載可聲明為private或者protected即可不需實現,但是不可聲明為public.因為如果申明為public不實現,雖然在該類域中達到了防拷貝,防賦值的效果,但是如果繼承之後實現了呢?此時依然可以在繼承類中拷貝構造和賦值,所以不可聲明為public.
shared_ptr的模擬實現
template<typename t=""> class SharedPtr { public: SharedPtr(T *ptr) :_ptr(ptr) ,_pcount(new int(1)) { cout<<"構造"<<endl; const="" t="">& sp) :_ptr(sp._ptr) ,_pcount(sp._pcount) { cout<<"拷貝構造"<<endl; t="">& operator=(SharedPtr<t>& sp) { cout<<"賦值操作符"<<endl; _pcount="sp._ptr;" _ptr="sp._ptr;" delete="" operator-="" return="">() { return _ptr; } T& operator*() { return *_ptr; } T* Get() { return _ptr; } int GetCount() { return *_pcount; } ~SharedPtr() { cout<<"析構了"<<endl; _pcount="0;" _ptr="0;" delete="" int="" pre="" private:="" t="">
shared_ptr解決多個指針指向同一塊空間會被析構多次這個問題的方法是引用計數,
在shared_ptr的模擬實現中設置了一個pcount指針用來記錄該空間被多少個指針所指向,當然在釋放該空間時只要該pcount不為0則只需要將該pcount空間的內容減減就可以了.是不是覺得shared_ptr很厲害呢?實際上當使用智能指針時最常用的就是shared_ptr,但是它也存在問題-循環引用的問題
請看下面一種場景:
struct Node { ~Node() { cout<<"~Node()"<<endl; int="" node=""> _next; boost::shared_ptr<node> _prev; }; void testshared_ptr() //循環引用的問題 { boost::shared_ptr<node> sp1(new Node); boost::shared_ptr<node> sp2(new Node); cout<<sp1.use_count()<<endl; 1="" sp1-="">_next=sp2; sp2->_prev=sp1; cout<<sp1.use_count()<<endl; 2="" pre="">
在定義雙向結點結構時,我們知道sp1,sp2,_next,_prev都是shared_ptr類型的智能指針,我們把這兩塊空間稱為n1,n2吧!sp1和sp2的_prev都指向n1,sp2和sp1的_next都指向n2,我們知道shared_ptr是采用引用計數的方法,那仫既然有兩個相同類型的智能指針都指向同一塊空間,而且空間n1,n2都是由兩個相同類型的智能指針指向的,那仫這兩個空間的引用計數器的內容都為2,那仫此時又會出現什仫問題呢?
如上圖所示,當銷毀該空間時會出現內存洩漏的問題,因為該塊空間n1分別是由兩個同類型的指針維護的,當銷毀n1時需要n2的引用計數降下來,而銷毀n2時需要n1的引用計數降下來,這就導致了無限循環的問題,這種場景讓我想到了踢皮球游戲:你踢給我,我踢給你,最後誰也得不到了.當然到最後這兩塊空間都是無法釋放的.為了解決shared_ptr中的循環引用問題,又引入了一個新的智能指針-weak_ptr
struct Node { ~Node() { cout<<"~Node()"<_next; boost::weak_ptr _prev; }; void testshared_ptr() //循環引用的問題 { boost::shared_ptr sp1(new Node); boost::shared_ptr sp2(new Node); cout< _next=sp2; sp2->_prev=sp1; cout<
weak_ptr是弱指針,它使得該引用計數不會增長;而且weak_ptr不可以單獨使用,必須與shared_ptr配套使用,因為weak_ptr的引入就是為了解決shared_ptr循環引用的問題