c++ 智能指针用法详解
http://www.cnblogs.com/TenosDoIt/p/3456704.html
本文介紹c++里面的四個智能指針: auto_ptr, shared_ptr, weak_ptr, unique_ptr 其中后三個是c++11支持,并且第一個已經(jīng)被c++11棄用。
為什么要使用智能指針:我們知道c++的內存管理是讓很多人頭疼的事,當我們寫一個new語句時,一般就會立即把delete語句直接也寫了,但是我們不能避免程序還未執(zhí)行到delete時就跳轉了或者在函數(shù)中沒有執(zhí)行到最后的delete語句就返回了,如果我們不在每一個可能跳轉或者返回的語句前釋放資源,就會造成內存泄露。使用智能指針可以很大程度上的避免這個問題,因為智能指針就是一個類,當超出了類的作用域是,類會自動調用析構函數(shù),析構函數(shù)會自動釋放資源。下面我們逐個介紹。
auto_ptr (官方文檔)
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | class?Test { public: ????Test(string s) ????{ ????????str = s; ???????cout<<"Test creat\n"; ????} ????~Test() ????{ ????????cout<<"Test delete:"<<str<<endl; ????} ????string& getStr() ????{ ????????return?str; ????} ????void?setStr(string s) ????{ ????????str = s; ????} ????void?print() ????{ ????????cout<<str<<endl; ????} private: ????string str; }; int?main() { ????auto_ptr<Test> ptest(new?Test("123")); ????ptest->setStr("hello "); ????ptest->print(); ????ptest.get()->print(); ????ptest->getStr() += "world !"; ????(*ptest).print(); ????ptest.reset(new?Test("123")); ????ptest->print(); ????return?0; } |
運行結果如下
如上面的代碼:智能指針可以像類的原始指針一樣訪問類的public成員,成員函數(shù)get()返回一個原始的指針,成員函數(shù)reset()重新綁定指向的對象,而原來的對象則會被釋放。注意我們訪問auto_ptr的成員函數(shù)時用的是“.”,訪問指向對象的成員時用的是“->”。我們也可用聲明一個空智能指針auto_ptr<Test>ptest();
當我們對智能指針進行賦值時,如ptest2 = ptest,ptest2會接管ptest原來的內存管理權,ptest會變?yōu)榭罩羔?#xff0c;如果ptest2原來不為空,則它會釋放原來的資源,基于這個原因,應該避免把auto_ptr放到容器中,因為算法對容器操作時,很難避免STL內部對容器實現(xiàn)了賦值傳遞操作,這樣會使容器中很多元素被置為NULL。判斷一個智能指針是否為空不能使用if(ptest == NULL),應該使用if(ptest.get() == NULL),如下代碼???????????????????????????????????????????????????????????本文地址
| 1 2 3 4 5 6 7 8 9 | int?main() { ????auto_ptr<Test> ptest(new?Test("123")); ????auto_ptr<Test> ptest2(new?Test("456")); ????ptest2 = ptest; ????ptest2->print(); ????if(ptest.get() == NULL)cout<<"ptest = NULL\n"; ????return?0; } |
還有一個值得我們注意的成員函數(shù)是release,這個函數(shù)只是把智能指針賦值為空,但是它原來指向的內存并沒有被釋放,相當于它只是釋放了對資源的所有權,從下面的代碼執(zhí)行結果可以看出,析構函數(shù)沒有被調用。
| 1 2 3 4 5 6 | int?main() { ????auto_ptr<Test> ptest(new?Test("123")); ????ptest.release(); ????return?0; } |
那么當我們想要在中途釋放資源,而不是等到智能指針被析構時才釋放,我們可以使用ptest.reset(); 語句。
unique_ptr (官方文檔)?
?
?
unique_ptr,是用于取代c++98的auto_ptr的產物,在c++98的時候還沒有移動語義(move semantics)的支持,因此對于auto_ptr的控制權轉移的實現(xiàn)沒有核心元素的支持,但是還是實現(xiàn)了auto_ptr的移動語義,這樣帶來的一些問題是拷貝構造函數(shù)和復制操作重載函數(shù)不夠完美,具體體現(xiàn)就是把auto_ptr作為函數(shù)參數(shù),傳進去的時候控制權轉移,轉移到函數(shù)參數(shù),當函數(shù)返回的時候并沒有一個控制權移交的過程,所以過了函數(shù)調用則原先的auto_ptr已經(jīng)失效了.在c++11當中有了移動語義,使用move()把unique_ptr傳入函數(shù),這樣你就知道原先的unique_ptr已經(jīng)失效了.移動語義本身就說明了這樣的問題,比較坑爹的是標準描述是說對于move之后使用原來的內容是未定義行為,并非拋出異常,所以還是要靠人肉遵守游戲規(guī)則.再一個,auto_ptr不支持傳入deleter,所以只能支持單對象(delete object),而unique_ptr對數(shù)組類型有偏特化重載,并且還做了相應的優(yōu)化,比如用[]訪問相應元素等.
unique_ptr 是一個獨享所有權的智能指針,它提供了嚴格意義上的所有權,包括:
1、擁有它指向的對象
2、無法進行復制構造,無法進行復制賦值操作。即無法使兩個unique_ptr指向同一個對象。但是可以進行移動構造和移動賦值操作
3、保存指向某個對象的指針,當它本身被刪除釋放的時候,會使用給定的刪除器釋放它指向的對象
unique_ptr 可以實現(xiàn)如下功能:
1、為動態(tài)申請的內存提供異常安全
2、講動態(tài)申請的內存所有權傳遞給某函數(shù)
3、從某個函數(shù)返回動態(tài)申請內存的所有權
4、在容器中保存指針
5、auto_ptr 應該具有的功能
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | unique_ptr<Test> fun() { ????return?unique_ptr<Test>(new?Test("789")); } int?main() { ????unique_ptr<Test> ptest(new?Test("123")); ????unique_ptr<Test> ptest2(new?Test("456")); ????ptest->print(); ????ptest2 = std::move(ptest);//不能直接ptest2 = ptest ????if(ptest == NULL)cout<<"ptest = NULL\n"; ????Test* p = ptest2.release(); ????p->print(); ????ptest.reset(p); ????ptest->print(); ????ptest2 = fun(); //這里可以用=,因為使用了移動構造函數(shù) ????ptest2->print(); ????return?0; } |
unique_ptr 和 auto_ptr用法很相似,不過不能使用兩個智能指針賦值操作,應該使用std::move; 而且它可以直接用if(ptest == NULL)來判斷是否空指針;release、get、reset等用法也和auto_ptr一致,使用函數(shù)的返回值賦值時,可以直接使用=, 這里使用c++11 的移動語義特性。另外注意的是當把它當做參數(shù)傳遞給函數(shù)時(使用值傳遞,應用傳遞時不用這樣),傳實參時也要使用std::move,比如foo(std::move(ptest))。它還增加了一個成員函數(shù)swap用于交換兩個智能指針的值
share_ptr?(官方文檔)
從名字share就可以看出了資源可以被多個指針共享,它使用計數(shù)機制來表明資源被幾個指針共享??梢酝ㄟ^成員函數(shù)use_count()來查看資源的所有者個數(shù)。出了可以通過new來構造,還可以通過傳入auto_ptr, unique_ptr,weak_ptr來構造。當我們調用release()時,當前指針會釋放資源所有權,計數(shù)減一。當計數(shù)等于0時,資源會被釋放。具體的成員函數(shù)解釋可以參考?here
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | int?main() { ????shared_ptr<Test> ptest(new?Test("123")); ????shared_ptr<Test> ptest2(new?Test("456")); ????cout<<ptest2->getStr()<<endl; ????cout<<ptest2.use_count()<<endl; ????ptest = ptest2;//"456"引用次數(shù)加1,“123”銷毀 ????ptest->print(); ????cout<<ptest2.use_count()<<endl;//2 ????cout<<ptest.use_count()<<endl;//2 ????ptest.reset(); ????ptest2.reset();//此時“456”銷毀 ????cout<<"done !\n"; ????return?0; } |
weak_ptr(官方文檔)
weak_ptr是用來解決shared_ptr相互引用時的死鎖問題,如果說兩個shared_ptr相互引用,那么這兩個指針的引用計數(shù)永遠不可能下降為0,資源永遠不會釋放。它是對對象的一種弱引用,不會增加對象的引用計數(shù),和shared_ptr之間可以相互轉化,shared_ptr可以直接賦值給它,它可以通過調用lock函數(shù)來獲得shared_ptr。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | class?B; class?A { public: ????shared_ptr<B> pb_; ????~A() ????{ ????????cout<<"A delete\n"; ????} }; class?B { public: ????shared_ptr<A> pa_; ????~B() ????{ ????????cout<<"B delete\n"; ????} }; void?fun() { ????shared_ptr<B> pb(new?B()); ????shared_ptr<A> pa(new?A()); ????pb->pa_ = pa; ????pa->pb_ = pb; ????cout<<pb.use_count()<<endl; ????cout<<pa.use_count()<<endl; } int?main() { ????fun(); ????return?0; } |
可以看到fun函數(shù)中pa ,pb之間互相引用,兩個資源的引用計數(shù)為2,當要跳出函數(shù)時,智能指針pa,pb析構時兩個資源引用計數(shù)會減一,但是兩者引用計數(shù)還是為1,導致跳出函數(shù)時資源沒有被釋放(A B的析構函數(shù)沒有被調用),如果把其中一個改為weak_ptr就可以了,我們把類A里面的shared_ptr<B> pb_; 改為weak_ptr<B> pb_; 運行結果如下,這樣的話,資源B的引用開始就只有1,當pb析構時,B的計數(shù)變?yōu)?,B得到釋放,B釋放的同時也會使A的計數(shù)減一,同時pa析構時使A的計數(shù)減一,那么A的計數(shù)為0,A得到釋放。
注意的是我們不能通過weak_ptr直接訪問對象的方法,比如B對象中有一個方法print(),我們不能這樣訪問,pa->pb_->print(); 英文pb_是一個weak_ptr,應該先把它轉化為shared_ptr,如:shared_ptr<B> p = pa->pb_.lock();??? p->print();
?
參考資料
胡健:http://www.cnblogs.com/hujian/archive/2012/12/10/2810776.html
胡健:http://www.cnblogs.com/hujian/archive/2012/12/10/2810754.html
胡健:http://www.cnblogs.com/hujian/archive/2012/12/10/2810785.html
天方:http://www.cnblogs.com/TianFang/archive/2008/09/20/1294590.html
gaa_ra:http://blog.csdn.net/gaa_ra/article/details/7841204
cplusplus:http://www.cplusplus.com/
?
【版權聲明】轉載請注明出處:http://www.cnblogs.com/TenosDoIt/p/3456704.html
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎
總結
以上是生活随笔為你收集整理的c++ 智能指针用法详解的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Socket网络编程--小小网盘程序(5
- 下一篇: 成都欢乐谷多带带收费项目