上一篇部落格我們簡單介紹了智能指標:簡單介紹智能指標
有關shared_ptr的循環參考問題可以參考部落格:weak_ptr–解決shared_ptr循環參考問題 auto_ptr 類比實現
template<class T>class Autoptr{public: Autoptr(T* ptr = NULL) :_ptr(ptr) {} Autoptr(Autoptr<T>& ap) { _ptr = ap._ptr; ap._ptr = NULL; } Autoptr<T>& operator=(Autoptr<T>& ap) { if (this != &ap) { delete _ptr; _ptr = ap._ptr; ap._ptr = NULL; } return *this; } T* operator->() { return _ptr; } T operator*() { return *_ptr; } ~Autoptr() { delete _ptr; _ptr = NULL; } void Reset(T* ptr = 0) { if (_ptr != ptr) { delete _ptr; } _ptr = ptr; }protected: T* _ptr;};void APTest(){ Autoptr<int> ap1(new int(10)); cout <<"*ap1 = "<< *ap1 << endl; Autoptr<int> ap2(ap1); cout <<"*ap2 = "<< *ap2 << endl; Autoptr<int> ap3(new int(20)); ap3 = ap2; cout <<"*ap3 = "<< *ap3 << endl; //cout << *ap1 << endl;//報錯}
測試結果:
程式碼分析
代碼中的Reset()函數是幹嘛的。
void Reset(T* ptr = 0) { if (_ptr != ptr) { delete _ptr; } _ptr = ptr; }
答:為了重設一個auto_ptr對象,必須使用reset()函數。可以向reset()傳遞一個指標,如果不希望設定該auto_ptr對象的話,可以傳遞一個0值。如果auto_ptr當前指向一個對象並且該auto_ptr對象擁有該對象的所有權,則該對象再底層指標重設之前,首先被刪除。例如:
void APTest(){ int *pi = new int(20); auto_ptr<int>_auto_ptr(pi); cout << "_auto_ptr = " << *pi << endl; int *pi2 = new int(30); _auto_ptr.reset(pi2); cout << "pi->" << *pi << endl; cout << "pi2->" << *pi2 << endl;}
輸出結果:
我們可以在輸出結果的第二行看到,pi已經變成了隨機值。reset()函數將auto_ptr對象原來指向的那塊動態分配的空間釋放了。
上面的代碼還有一句:
//cout << *ap1 << endl;//報錯
為什麼報錯。。
這是因為ap1已經把它指向的空間交給了ap2去管理,所以ap1已經不具備訪問原來自己所指向的空間的許可權。所以對它進行解引用是非法的。
所以可以看清auto_ptr的本質是管理權的轉移,即ap1將自己所指向的空間交給ap2來管理,析構也是由ap2來完成。 scoped_ptr
由於auto_ptr的嚴重缺陷,所以後來引入了scoped_ptr。防拷貝,意思就是不能進行拷貝,簡單地說是一種簡單粗暴的方式。 類比實現
template<class T>class Scopedptr{public: Scopedptr(T* ptr = NULL) :_ptr(ptr) {} T* operator->() { return _ptr; } T operator*() { return *_ptr; } ~Scopedptr() { delete _ptr; _ptr = NULL; }protected: Scopedptr(Scopedptr<T>& sp); Scopedptr<T>& operator=(const Scopedptr<T>& p);private: T* _ptr;};void SPTest(){ Scopedptr<int> sp(new int(10)); cout << "sp = " << *sp << endl;}
scoped_ptr中對拷貝建構函式和賦值運算子的重載函數只是進行了聲明,並沒有去定義這兩個函數,而且聲明為protected或者是private,這是防止別人在類外對這兩個函數進行定義。防止拷貝,所以說scope_dptr是一種簡單粗暴的方式。 shared_ptr
編寫程式往往要用到拷貝,這樣scopedptr就不能起到相應的作用,所以便有了shared_ptr。
shared_ptr->採用了引用計數,優點是功能強大,但是也有缺點,缺點是過於複雜,而且會引起循環參考。
template<class T>class Sharedptr{public: Sharedptr(T* ptr = NULL) :_ptr(ptr) , pcount(0) { if (_ptr != NULL) pcount = new int(1); } Sharedptr(const Sharedptr<T>& sp) { _ptr = sp._ptr; pcount = sp.pcount; ++(*pcount); } Sharedptr<T>& operator=(const Sharedptr<T>& sp) { if (this != &sp) { if (_ptr && --(*pcount) == 0) { delete pcount; delete _ptr; } pcount = sp.pcount; _ptr = sp._ptr; ++(*pcount); } return *this; } ~Sharedptr() { if (_ptr && --(*pcount) == 0) { delete _ptr; delete pcount; } } T& operator*() { return *_ptr; } T* operator->() { return _ptr; } T Getpcount() { return *(pcount); } void Realease() { if (--(*pcount) == 0) { delete _pcount; delete _ptr; } } void Reset(T* ptr, int* pcount) { if (_ptr != ptr) { delete _ptr; delete _pcount; } _ptr = ptr; _pcount = pcount; }private: T* _ptr; T* pcount;};void Test(){ Sharedptr<int> s1(new int(10)); cout << "s1 = " << *s1 << endl; cout << "pcount = " << s1.Getpcount() << endl; Sharedptr<int> s2(s1); cout << "s2 = " << *s2 << endl; cout << "pcount = " << s2.Getpcount() << endl; Sharedptr<int> s3; s3 = s1; cout << "s3 = " << *s3 << endl; cout << "pcount = " << s3.Getpcount() << endl;}
測試結果: