看到了迭代器這裡,想到了應該把智能指針的知識總結一下了,我實現了三種智能指針,分別是auto_ptr,scoped_ptr,shared_ptr命名是根據boost庫中的智能指針命名的
什麼是智能指針?智能指針可以幫助你在忘記釋放new出來的內存的時候自動幫助你釋放內存 可以有效避免內存洩漏 例如當異常出現,跳轉之後。。。內存是應該被釋放的呀,一直抓住人家不放會造成內存洩漏哦 智能指針就是RAII(資源分配即初始化)一種典型的應用 利用類的構造函數和析構函數來進行內存的開辟和釋放,智能指針不能完全說成是指針,它是一種類型用來管理指針的釋放,當出了作用域之後,就回去進行內存釋放 為了要讓智能指針更像一個指針,需要對運算符進行重載:例如*引用 以上就是auto_ptr的部分代碼,但是有個很明顯的問題,那就是沒有拷貝構造函數 當沒有書寫拷貝構造函數使用默認的拷貝構造函數的時候,這種語句就很危險了,涉及到深淺拷貝的問題,如何解決這種問題呢? 下面就講!~ auto_ptr ——》自動釋放指針(大坑貨):拷貝賦值之後,將前一個對象置空,這就完成了只釋放一次(當出現拷貝的情況,兩個指針分別釋放其所指向的同一個空間就會boom!) scoped_ptr——》守衛——》防拷貝:簡單粗暴利用pravate不給你訪問權限,不允許拷貝(既然拷貝會出錯,那不拷貝不就沒事了?) shared_ptr——》共享——》引用計數:采用指針引用計數的方式進行,不采用普通類型,也不用靜態變量,就是要用指針(其實最推薦使用的居然是仿拷貝指針,就是說,沒必要拷貝就別拷貝了,必須拷貝再用共享指針) 貼出代碼!1 class AutoPtr 2 { 3 public: 4 explicit AutoPtr(T* ptr=NULL) 5 :_ptr(ptr) 6 { 7 } 8 AutoPtr(AutoPtr& ap) 9 :_ptr(ap._ptr) 10 { 11 ap._ptr = NULL; 12 } 13 ~AutoPtr() 14 { 15 delete _ptr; 16 } 17 AutoPtr& operator=(AutoPtr& ap) 18 { 19 if (_ptr != ap._ptr) 20 { 21 if (NULL == _ptr) 22 { 23 _ptr = ap._ptr; 24 ap._ptr = NULL; 25 } 26 else 27 { 28 delete _ptr; 29 _ptr = ap._ptr; 30 ap._ptr = NULL; 31 } 32 } 33 return *this; 34 } 35 T& operator *() 36 { 37 return *_ptr; 38 } 39 T* operator->() 40 { 41 return _ptr; 42 } 43 T* GetPtr() 44 { 45 return _ptr; 46 } 47 private: 48 T* _ptr; 49 }; 50 51 52 53 void test1() 54 { 55 AutoPtr<int > ap1 = new int;//支持強轉(這裡的意思是,new產生的int *會被強轉成auto_ptr指針,就是說會拿int *構造臨時的auto_ptr變量然後再賦值) 56 AutoPtr<int > ap2 = new int; 57 //AutoPtr<int >ap3(ap1);//當心,深淺拷貝 58 AutoPtr<int > ap4; 59 ap4 = ap1; 60 ap2 = ap1; 61 /*int *p1 = new int; 62 int *p2 = new int; 63 delete p1; 64 delete p2;*/ 65 }
PS:最長注釋的那句AutoPtr<int > ap1 = new int;如果不希望這種事情發生的話要用到explicit關鍵字
我把測試用例也貼出來了~auto_ptr的實現還是非常簡單的,但是就是太坑了,最好不要使用~
接下來是防拷貝智能指針scoped_ptr
1 #include<iostream> 2 using namespace std; 3 4 5 6 template<class T> 7 class ScopedPtr 8 { 9 public: 10 explicit ScopedPtr(T* ptr=NULL) 11 :_ptr(ptr) 12 { 13 } 14 ~ScopedPtr() 15 { 16 if (_ptr) 17 { 18 delete _ptr; 19 } 20 } 21 T* operator ->() 22 { 23 return _ptr; 24 } 25 T& operator *() 26 { 27 return *_ptr; 28 } 29 T* GetPtr() 30 { 31 return _ptr; 32 } 33 34 private: 35 ScopedPtr(ScopedPtr& sp) 36 { 37 38 } 39 ScopedPtr& operator=() 40 { 41 42 } 43 private: 44 T* _ptr; 45 }; 46 47 48 49 void test1() 50 { 51 52 ScopedPtr<int> sp1 = new int(1); 53 ScopedPtr<int> sp2 = new int(2); 54 ScopedPtr<int> sp3(sp1); 55 }
執行這個代碼的話就會報編譯錯誤啦,因為拷貝構造放在私有成員裡了,是不能使用哒,實現也非常簡單,就把關於拷貝的東西全都丟給private就對了
接下來是共享指針shared_ptr1 #include<iostream> 2 using namespace std; 3 template<class T> 4 class SharedPtr 5 { 6 public: 7 explicit SharedPtr(T* ptr) 8 :_ptr(ptr) 9 ,_pCount(new int(1)) 10 { 11 12 } 13 SharedPtr(SharedPtr& sp) 14 :_ptr(sp._ptr) 15 ,_pCount(sp._pCount) 16 { 17 (*_pCount)++; 18 } 19 ~SharedPtr() 20 { 21 if (--(*_pCount) == 0) 22 { 23 delete _ptr; 24 delete _pCount; 25 } 26 } 27 SharedPtr& operator=(SharedPtr& sp) 28 { 29 if (_ptr != sp._ptr) 30 { 31 if (NULL == _ptr) 32 { 33 _ptr = sp._ptr; 34 _pCount = sp._pCount; 35 } 36 else 37 { 38 _Release(); 39 } 40 } 41 return *this; 42 } 43 T& operator*() 44 { 45 return *_ptr; 46 } 47 T* operator->() 48 { 49 return _ptr; 50 } 51 protected: 52 void _AddRef() 53 { 54 ++(*_pCount); 55 } 56 void _Release() 57 { 58 if (--(*_pCount) == 0) 59 { 60 delete _ptr; 61 delete _pCount; 62 _ptr = NULL; 63 _pCount = NULL; 64 } 65 } 66 private: 67 T* _ptr; 68 int* _pCount; 69 };
引用計數是用指針來實現的,一開始采用的是靜態變量,但是有個問題,當聲明
1 SharedPtr<int> s = new int(20); 2 SharedPtr<int> s1=new int (30);
因為存儲的數值不同,引用計數應該是不會增長的,但是由於采用了靜態變量,該類對象都使用了這個引用計數,就會都往引用計數上加,這就錯了
采用指針,開辟出獨有的一塊空間來管理計數顯然才是我們所需要的,析構的時候把delete掉的指針賦空會更好~所以有了Release函數
應該在構造函數之前加上explicit關鍵字,是為了防止將指針經過轉換構造函數變成智能指針,就是防止 int *ptr; autoptr<int> a; a=ptr; 這樣的轉換發生