內存管理是一個比較繁瑣的問題,C++中有兩個實現方案: 垃圾回收機制和智能指針。垃圾回收機制由於性能等緣由不被C++的大佬們推崇, 而智能指針被認爲是解決C++內存問題的最優方案。html
一個智能指針就是一個C++的對象, 這對象的行爲像一個指針,可是它卻能夠在其不須要的時候自動刪除。注意這個「其不須要的時候」, 這可不是一個精確的定義。這個不須要的時候能夠指好多方面:局部變量退出函數做用域、類的對象被析構……。因此boost定義了多個不一樣的智能指針來管理不一樣的場景。ios
shared_ptr<T> | 內部維護一個引用計數器來判斷此指針是否是須要被釋放。是boost中最經常使用的智能指針了。 |
scoped_ptr<t> | 當這個指針的做用域消失以後自動釋放 |
intrusive_ptr<T> | 也維護一個引用計數器,比shared_ptr有更好的性能。可是要求T本身提供這個計數器。 |
weak_ptr<T> | 弱指針,要和shared_ptr 結合使用 |
shared_array<T> | 和shared_ptr類似,可是訪問的是數組 |
scoped_array<T> | 和scoped_ptr類似,可是訪問的是數組 |
scoped_ptr 是boost中最簡單的智能指針。scoped_ptr的目的也是很簡單, 當一個指針離開其做用域時候,釋放相關資源。特別注意的必定就是scoped_ptr 不能共享指針的全部權也不能轉移全部權。也就是說這個內存地址就只能給的聲明的變量用,不能給其餘使用。c++
(1)scoped_ptr的效率和空間的消耗內置的指針差很少程序員
(2)scoped_ptr不能用於管理數組對象,不能指向一塊可以動態增加的內存區域(用scoped_array代替)數組
(3)scoped_ptr不能轉換全部權,所以不能做爲函數的返回值安全
(4)scoped_ptr不能共享全部權,所以不能用於stl的容器中(用shared_ptr代替)多線程
(1)在可能有異常拋出的做用域裏使用指針ide
(2)函數裏有幾條控制路徑函數
(3)動態分配對象的生存期應被限制於特定的做用域內性能
(4)異常安全很是重要時(總應如此!)
1 class test 2 { 3 public: 4 void print() 5 { 6 cout << "test print now" <<endl; 7 } 8 }; 9 int _tmain(int argc, _TCHAR* argv[]) 10 { 11 boost::scoped_ptr<test> x(new test); 12 x->print(); 13 return 0; 14 }
boost::shared_ptr是能夠共享全部權的智能指針.
(1)boost::shared_ptr在內部維護一個引用計數器, 當有一個指針指向這塊內存區域是引用計數+1, 反之-1, 若是沒有任何指針指向這塊區域, 引用計數器爲0,釋放內存區域。
(2)boost::shared_ptr能夠共享和轉移全部權
(3)boost::shared_ptr能夠被標準庫的容器所使用
(4)boost::shared_ptr是線程安全的,這點在多線程程序中也很是重要
(5)boost::shared_ptr不能指向一塊動態增加的內存(用share_array代替)
(1)避免對shared_ptr所管理的對象的直接內存管理操做,以避免形成該對象的重釋放
(2)shared_ptr並不能對循環引用的對象內存自動管理(這點是其它各類引用計數管理內存方式的通病)
(3)不要構造一個臨時的shared_ptr做爲函數的參數
1 int _tmain(int argc, _TCHAR* argv[]) 2 { 3 boost::shared_ptr<test> ptr_1(new test); 4 ptr_1->print();//引用計數爲1 5 boost::shared_ptr<test> ptr_2 = ptr_1; 6 ptr_2->print();//引用計數爲2 7 ptr_1->print();// 引用計數仍是爲2 8 return 0; 9 }
boost::intrusive_ptr一種「侵入式」的引用計數指針,它實際並不提供引用計數功能,而是要求被存儲的對象本身實現引用計數功能,並提供intrusive_ptr_add_ref和intrusive_ptr_release函數接口供boost::intrusive_ptr調用。
(1)你須要把 this看成智能指針來使用
(2)已有代碼使用或提供了插入式的引用計數
(3)智能指針的大小必須與裸指針的大小相等
下面經過一個具體的例子來講明boost::intrusive_ptr的用法,首先實現一個基類intrusive_ptr_base,定義intrusive_ptr_add_ref和intrusive_ptr_release函數來提供引用計數功能。
1 /** 2 * intrusive_ptr_base基類,提供intrusive_ptr_add_ref()和intrusive_ptr_release()函數來提供引用計數功能; 3 * 使用boost::intrusive_ptr指針存儲的用戶類類型必須繼承自intrusive_ptr_base基類。 4 */ 5 #include <ostream> 6 #include <boost/checked_delete.hpp> 7 #include <boost/detail/atomic_count.hpp> 8 9 10 template<class T> 11 class intrusive_ptr_base { 12 public: 13 /** 14 * 缺省構造函數 15 */ 16 intrusive_ptr_base(): ref_count(0) { 17 std::cout << " Default constructor " << std::endl; 18 } 19 20 /** 21 * 不容許拷貝構造,只能使用intrusive_ptr來構造另外一個intrusive_ptr 22 */ 23 intrusive_ptr_base(intrusive_ptr_base<T> const&): ref_count(0) { 24 std::cout << " Copy constructor..." << std::endl; 25 } 26 27 /** 28 * 不容許進行賦值操做 29 */ 30 intrusive_ptr_base& operator=(intrusive_ptr_base const& rhs) { 31 std::cout << " Assignment operator..." << std::endl; 32 return *this; 33 } 34 35 /** 36 * 遞增引用計數(放到基類中以便compiler能找到,不然須要放到boost名字空間中) 37 */ 38 friend void intrusive_ptr_add_ref(intrusive_ptr_base<T> const* s) { 39 std::cout << " intrusive_ptr_add_ref..." << std::endl; 40 assert(s->ref_count >= 0); 41 assert(s != 0); 42 ++s->ref_count; 43 } 44 45 /** 46 * 遞減引用計數 47 */ 48 friend void intrusive_ptr_release(intrusive_ptr_base<T> const* s) { 49 std::cout << " intrusive_ptr_release..." << std::endl; 50 assert(s->ref_count > 0); 51 assert(s != 0); 52 if (--s->ref_count == 0) 53 boost::checked_delete(static_cast<T const*>(s)); //s的實際類型就是T,intrusive_ptr_base<T>爲基類 54 } 55 56 /** 57 * 相似於shared_from_this()函數 58 */ 59 boost::intrusive_ptr<T> self() { 60 return boost::intrusive_ptr<T>((T*)this); 61 } 62 63 boost::intrusive_ptr<const T> self() const { 64 return boost::intrusive_ptr<const T>((T const*)this); 65 } 66 67 int refcount() const { 68 return ref_count; 69 } 70 71 private: 72 ///should be modifiable even from const intrusive_ptr objects 73 mutable boost::detail::atomic_count ref_count; 74 75 };
用戶類類型須要繼承intrusive_ptr_base基類,以便具備引用計數功能。
1 #include <iostream> 2 #include <string> 3 #include <boost/intrusive_ptr.hpp> 4 #include "intrusive_ptr_base.hpp" 5 6 /** 7 * 用戶類類型繼承自intrusive_ptr_base,該實現方式相似於boost::enable_shared_from_this<Y> 8 */ 9 class Connection : public intrusive_ptr_base< Connection > { 10 public: 11 /** 12 * 構造函數,調用intrusive_ptr_base< Connection >的缺省構造函數來初始化對象的基類部分 13 */ 14 Connection(int id, std::string tag): 15 connection_id( id ), connection_tag( tag ) {} 16 17 /** 18 * 拷貝構造函數,只複製自身數據,不能複製引用計數部分 19 */ 20 Connection(const Connection& rhs): 21 connection_id( rhs.connection_id ), connection_tag( rhs.connection_tag) {} 22 23 /** 24 * 賦值操做,一樣不能複製引用計數部分 25 */ 26 const Connection operator=( const Connection& rhs) { 27 if (this != &rhs) { 28 connection_id = rhs.connection_id; 29 connection_tag = rhs.connection_tag; 30 } 31 32 return *this; 33 } 34 35 private: 36 int connection_id; 37 std::string connection_tag; 38 }; 39 40 int main() { 41 std::cout << "Create an intrusive ptr" << std::endl; 42 boost::intrusive_ptr< Connection > con0 (new Connection(4, "sss") ); //調用intrusive_ptr_add_ref()遞增引用計數 43 std::cout << "Create an intrusive ptr. Refcount = " << con0->refcount() << std::endl; 44 45 boost::intrusive_ptr< Connection > con1 (con0); //調用intrusive_ptr_add_ref() 46 std::cout << "Create an intrusive ptr. Refcount = " << con1->refcount() << std::endl; 47 boost::intrusive_ptr< Connection > con2 = con0; //調用intrusive_ptr_add_ref() 48 std::cout << "Create an intrusive ptr. Refcount = " << con2->refcount() << std::endl; 49 50 std::cout << "Destroy an intrusive ptr" << std::endl; 51 52 return 0; 53 }
程序運行輸出:
Create an intrusive ptr
Default constructor
intrusive_ptr_add_ref...
Create an intrusive ptr. Refcount = 1
intrusive_ptr_add_ref...
Create an intrusive ptr. Refcount = 2
intrusive_ptr_add_ref...
Create an intrusive ptr. Refcount = 3
Destroy an intrusive ptr
intrusive_ptr_release...
intrusive_ptr_release...
intrusive_ptr_release...
使用boost::shared_ptr用戶類自己不須要具備引用計數功能,而是由boost::shared_ptr來提供;使用boost::shared_ptr的一大陷阱就是用一個raw pointer屢次建立boost::shared_ptr,這將致使boost::shared_ptr析構時該raw pointer被屢次銷燬當。即不能以下使用:
1 int *a = new int(5); 2 boost::shared_ptr ptr1(a); 3 boost::shared_ptr ptr2(a); //錯誤!
boost::intrusive_ptr徹底具有boost::shared_ptr的功能,且不存在shared_ptr的問題,便可以利用raw pointer建立多個intrusive _ptr,其緣由就在於引用計數的ref_count對象,shared_ptr是放在shared_ptr結構裏,而目標對象T經過繼承intrusive_ptr_base將引用計數做爲T對象的內部成員變量,就不會出現同一個對象有兩個引用計數器的狀況出現。
那麼爲何一般鼓勵你們使用shared_ptr,而不是intrusive_ptr呢, 在於shared_ptr不是侵入性的,能夠指向任意類型的對象; 而intrusive_ptr所要指向的對象,須要繼承intrusive_ptr_base,即便不須要,引用計數成員也會被建立。
若是建立新類且須要進行傳遞,則繼承intrusive_ptr_base,使用intrusive_ptr。
weak_ptr 就是一個弱指針。weak_ptr 被shared_ptr控制, 它能夠經過share_ptr的構造函數或者lock成員函數轉化爲share_ptr。
(1)weak_ptr的一個最大特色就是它共享一個share_ptr的內存
(2)不管是構造仍是析構一個weak_ptr都不會影響引用計數器
一個強引用當被引用的對象活着的話,這個引用也存在(就是說,當至少有一個強引用,那麼這個對象就不能被釋放)。boost::share_ptr就是強引用。相對而言,弱引用當引用的對象活着的時候不必定存在。僅僅是當它存在的時候的一個引用。弱引用並不修改該對象的引用計數,這意味這弱引用它並不對對象的內存進行管理,在功能上相似於普通指針,然而一個比較大的區別是,弱引用能檢測到所管理的對象是否已經被釋放,從而避免訪問非法內存。
1 boost::weak_ptr 2 3 boost::weak_ptr<T>是boost提供的一個弱引用的智能指針,它的聲明能夠簡化以下: 4 5 namespace boost { 6 7 template<typename T> class weak_ptr { 8 public: 9 template <typename Y> 10 weak_ptr(const shared_ptr<Y>& r); 11 12 weak_ptr(const weak_ptr& r); 13 14 ~weak_ptr(); 15 16 T* get() const; 17 bool expired() const; 18 shared_ptr<T> lock() const; 19 }; 20 }
能夠看到,boost::weak_ptr必須從一個boost::share_ptr或另外一個boost::weak_ptr轉換而來,這也說明,進行該對象的內存管理的是那個強引用的boost::share_ptr。boost::weak_ptr只是提供了對管理對象的一個訪問手段。
boost::weak_ptr除了對所管理對象的基本訪問功能(經過get()函數)外,還有兩個經常使用的功能函數:expired()用於檢測所管理的對象是否已經釋放;lock()用於獲取所管理的對象的強引用指針。
引用計數是一種便利的內存管理機制,但它有一個很大的缺點,那就是不能管理循環引用的對象。
1 #include <string> 2 #include <iostream> 3 #include <boost/shared_ptr.hpp> 4 #include <boost/weak_ptr.hpp> 5 6 class parent; 7 class children; 8 9 typedef boost::shared_ptr<parent> parent_ptr; 10 typedef boost::shared_ptr<children> children_ptr; 11 12 class parent 13 { 14 public: 15 ~parent() { std::cout <<"destroying parent\n"; } 16 17 public: 18 children_ptr children; 19 }; 20 21 class children 22 { 23 public: 24 ~children() { std::cout <<"destroying children\n"; } 25 26 public: 27 parent_ptr parent; 28 }; 29 30 31 void test() 32 { 33 parent_ptr father(new parent()); 34 children_ptr son(new children); 35 36 father->children = son; 37 son->parent = father; 38 } 39 40 void main() 41 { 42 std::cout<<"begin test...\n"; 43 test(); 44 std::cout<<"end test.\n"; 45 }
運行該程序能夠看到,即便退出了test函數後,因爲parent和children對象互相引用,它們的引用計數都是1,不能自動釋放,而且此時這兩個對象再沒法訪問到。這就引發了c++中那臭名昭著的內存泄漏。
通常來說,解除這種循環引用有下面有三種可行的方法:
(1)當只剩下最後一個引用的時候須要手動打破循環引用釋放對象。
(2)當parent的生存期超過children的生存期的時候,children改成使用一個普通指針指向parent。
(3)使用弱引用的智能指針打破這種循環引用。
雖然這三種方法均可行,但方法1和方法2都須要程序員手動控制,麻煩且容易出錯。這裏主要介紹一下第三種方法和boost中的弱引用的智能指針boost::weak_ptr。
因爲弱引用不更改引用計數,相似普通指針,只要把循環引用的一方使用弱引用,便可解除循環引用。對於上面的那個例子來講,只要把children的定義改成以下方式,便可解除循環引用:
1 class children 2 { 3 public: 4 ~children() { std::cout <<"destroying children\n"; } 5 6 public: 7 boost::weak_ptr<parent> parent; 8 };
最後值得一提的是,雖然經過弱引用指針能夠有效的解除循環引用,但這種方式必須在程序員能預見會出現循環引用的狀況下才能使用,也能夠是說這個僅僅是一種編譯期的解決方案,若是程序在運行過程當中出現了循環引用,仍是會形成內存泄漏的。所以,不要認爲只要使用了智能指針便能杜絕內存泄漏。畢竟,對於C++來講,因爲沒有垃圾回收機制,內存泄漏對每個程序員來講都是一個很是頭痛的問題。
(1)要打破遞歸的依賴關係
(2)使用一個共享的資源而不須要共享全部權
(3)避免懸空的指針
(4)shared_ptr構造weak_ptr時,weak_ptr所指內存爲空會拋出異常,而weak_ptr的lock()成員不會拋出異常但會返回個空指針,根據本身需求選擇
1 int _tmain(int argc, _TCHAR* argv[]) 2 { 3 boost::shared_ptr<test> sharePtr(new test);; 4 boost::weak_ptr<test> weakPtr(sharePtr); 5 //weakPtr 就是用來保存指向這塊內存區域的指針的 6 //幹了一大堆其餘事情 7 boost::shared_ptr<test> sharePtr_2 = weakPtr.lock(); 8 if (sharePtr_2) 9 sharePtr_2->print(); 10 return 0; 11 }
前面提到過shared_ptr和scoped_ptr不能用於數組的內存(new []),因此shared_array和scoped_array就是他們的代替品。
1 int _tmain(int argc, _TCHAR* argv[]) 2 { 3 const int size = 10; 4 boost::shared_array<test> a(new test[]); 5 for (int i = 0; i < size; ++i) 6 a[i].print(); 7 return 0; 8 }
auto_ptr是C++標準庫裏的類,它接受一個類型形參的模板,爲動態分配的對象提供異常安全。其實,它的核心思想是:用一個對象存儲須要被自動釋放的資源,而後依靠對象的析構函數來釋放資源。
(1)auto_ptr的構造函數帶explicit 關鍵字,必須使用初始化的直接形式來建立auto_ptr對象。
1 auto_ptr<int> ap(new int(1024)); //ok 2 auto_ptr<int> ap=new int(1024); //error
(2)auto_ptr 在析構函數中釋放了動態分配的空間,所以能自動釋放內存。下面函數只動態分配了內存,並無顯示釋放。可是編譯器保證在展開棧越過f以前運行pi的析構函數。
1 void f() { auto_ptr<int> ap(new int(1024)); }
(3)auto_ptr重載瞭解引用操做符和箭頭操做符,支持了普通指針的行爲。
(4)賦值時刪除了左操做數指向的對象
1 auto_ptr<int> ap1(new int(1024)); auto_ptr<int> ap2; 2 ap2=ap1;
將ap1賦值給ap2後,刪除了ap2原來指的對象;ap2置爲指向ap1所指的對象;ap1爲未綁定對象。可看代碼。
(5)測試auto_ptr對象,能夠調用get成員函數,該函數返回包含在auto_ptr對象中的基礎指針。
1 if(ap.get()) *ap=512; //ok 2 if(ap) *ap=512; //error
儘管auto_ptr類模板爲處理動態分配的內存提供了安全性和便利性的尺度,可是也存在很多缺陷,接下來結合例子給出auto_ptr的一些缺陷。
(1)不要使用auto_ptr對象保存指向靜態分配對象的指針。不然,當auto_ptr對象自己被撤銷時,它將試圖刪除指向非動態分配對象的指針,致使未定義的行爲。
1 int a=1; 2 auto_ptr<int> ap(&a); //編譯沒有問題,會致使未定義行爲
(2)不要使兩個auto_ptr對象指向同一對象。
1 auto_ptr<int> ap1(new int (1024)); 2 auto_ptr<int> ap2(ap1.get());
(3)不要使用auto_ptr對象保存指向動態分配數組的指針。從源代碼中能夠看出,它用的是delete操做符,而不是delete [ ] 操做符
(4)不要將auto_ptr對象存儲在容器中。由於auto_ptr的複製和賦值具備破壞性。不知足容器要求:複製或賦值後,兩個對象必須具備相同值。
1 #include <utility> 2 #include <iostream> 3 using namespace std; 4 5 class A 6 { 7 public: 8 A() { id = ++count; cout << "create A" << id << "\n"; } 9 ~A() { cout << "destroy A" << id << "\n"; } 10 private: 11 static int count; 12 int id; 13 }; 14 15 int A::count = 0; 16 17 /*調用該函數會丟失掉全部權*/ 18 void sink(auto_ptr<A> a) 19 { 20 cout << "Enter sink()\n"; 21 } 22 23 /*調用該函數會建立對象,並獲取全部權*/ 24 auto_ptr<A> create() 25 { 26 cout << "Enter create()\n"; 27 auto_ptr<A> a(new A()); 28 return a; 29 } 30 31 int main(int argc, char *argv[]) 32 { 33 auto_ptr<A> a1 = create(); 34 auto_ptr<A> a2 = a1; /*轉移全部權,此時a1無效了*/ 35 auto_ptr<A> a3(new A()); 36 cout << "Exit create()\n"; 37 sink(a2);/*丟失全部權,會發現a2的釋放在sink函數中進行*/ 38 cout << "Exit sink()\n"; 39 return 0; 40 } 41 42 輸出結果是:<br>Enter create()<br>create A1<br>create A2<br>Exit create()<br>Enter sink()<br>destroy A1<br>Exit sink()<br>destroy A2<br><br>
(1)聲明一個智能指針的時候要當即給它實例化, 並且必定不能手動釋放它。
(2)…_ptr<T> 不是T* 類型。因此:
a: 聲明的時候要…_ptr<T> 而不是….._ptr<T*>。
b:不能把T* 型的指針賦值給它。
c: 不能寫ptr=NULL, 而用ptr.reset()代替。
(3)不能循環引用。
(4)不要聲明臨時的share_ptr, 而後把這個指針傳遞給一個函數。
原文連接:http://www.cnblogs.com/sld666666/archive/2010/12/16/1908265.html