C++中auto_ptr智能指針的用法詳解
智能指針(auto_ptr) 這個(gè)名字聽起來很酷是不是?其實(shí)auto_ptr 只是C++標(biāo)準(zhǔn)庫(kù)提供的一個(gè)類模板,它與傳統(tǒng)的new/delete控制內(nèi)存相比有一定優(yōu)勢(shì),但也有其局限。本文總結(jié)的8個(gè)問題足以涵蓋auto_ptr的大部分內(nèi)容。
auto_ptr是什么?
auto_ptr 是C++標(biāo)準(zhǔn)庫(kù)提供的類模板,auto_ptr對(duì)象通過初始化指向由new創(chuàng)建的動(dòng)態(tài)內(nèi)存,它是這塊內(nèi)存的擁有者,一塊內(nèi)存不能同時(shí)被分給兩個(gè)擁有者。當(dāng)auto_ptr對(duì)象生命周期結(jié)束時(shí),其析構(gòu)函數(shù)會(huì)將auto_ptr對(duì)象擁有的動(dòng)態(tài)內(nèi)存自動(dòng)釋放。即使發(fā)生異常,通過異常的棧展開過程也能將動(dòng)態(tài)內(nèi)存釋放。auto_ptr不支持new 數(shù)組。
C++中指針申請(qǐng)和釋放內(nèi)存通常采用的方式是new和delete。然而標(biāo)準(zhǔn)C++中還有一個(gè)強(qiáng)大的模版類就是auto_ptr,它可以在你不用的時(shí)候自動(dòng)幫你釋放內(nèi)存。下面簡(jiǎn)單說一下用法。
<textarea cols="50" rows="15" name="code" class="cpp">
用法一: std::auto_ptr<MyClass>m_example(new MyClass());
用法二: std::auto_ptr<MyClass>m_example; m_example.reset(new MyClass());
用法三(指針的賦值操作): std::auto_ptr<MyClass>m_example1(new MyClass());
std::auto_ptr<MyClass>m_example2(new MyClass()); m_example2=m_example1;</textarea>
則C++會(huì)把m_example所指向的內(nèi)存回收,使m_example1 的值為NULL,所以在C++中,應(yīng)絕對(duì)避免把a(bǔ)uto_ptr放到容器中。即應(yīng)避免下列代碼:
vector<auto_ptr<MyClass>>m_example;
當(dāng)用算法對(duì)容器操作的時(shí)候,你很難避免STL內(nèi)部對(duì)容器中的元素實(shí)現(xiàn)賦值傳遞,這樣便會(huì)使容器中多個(gè)元素被置位NULL,而這不是我們想看到的。
雖然,標(biāo)準(zhǔn)auto_ptr智能指針機(jī)制很多人都知道,但很少使用它。這真是個(gè)遺憾,因?yàn)閍uto_ptr優(yōu)雅地解決了C++設(shè)計(jì)和編碼中常見的問題,正確地使用它可以生成健壯的代碼。本文闡述了如何正確運(yùn)用auto_ptr來讓你的代碼更加安全——以及如何避免對(duì)auto_ptr危險(xiǎn)但常見的誤用,這些誤用會(huì)引發(fā)間斷性發(fā)作、難以診斷的bug。
為什么稱它為“自動(dòng)”指針?auto_ptr只是眾多可能的智能指針之一。許多商業(yè)庫(kù)提供了更復(fù)雜的智能指針,用途廣泛而令人驚異,從管理引用的數(shù)量到提供先進(jìn)的代理服務(wù)??梢园褬?biāo)準(zhǔn)C++ auto_ptr看作智能指針的Ford Escort(elmar注:可能指福特的一種適合家居的車型):一個(gè)簡(jiǎn)易、通用的智能指針,它不包含所有的小技巧,不像專用的或高性能的智能指針那么奢華,但是它可以很好的完成許多普遍的工作,它很適合日常性的使用。
auto_ptr所做的事情,就是動(dòng)態(tài)分配對(duì)象以及當(dāng)對(duì)象不再需要時(shí)自動(dòng)執(zhí)行清理。這里是一個(gè)簡(jiǎn)單的代碼示例,沒有使用auto_ptr所以不安全:
<textarea cols="50" rows="15" name="code" class="cpp">// 示例1(a):原始代碼 void f() { T* pt( new T ); /*...更多的代碼...*/ delete pt; }</textarea>
我們大多數(shù)人每天寫類似的代碼。如果f()函數(shù)只有三行并且不會(huì)有任何意外,這么做可能挺好的。但是如果f()從不執(zhí)行delete語(yǔ)句,或者是由于過早的返回,或者是由于執(zhí)行函數(shù)體時(shí)拋出了異常,那么這個(gè)被分配的對(duì)象就沒有被刪除,從而我們產(chǎn)生了一個(gè)經(jīng)典的內(nèi)存泄漏。
能讓示例1(a)安全的簡(jiǎn)單辦法是把指針封裝在一個(gè)“智能的”類似于指針的對(duì)象里,這個(gè)對(duì)象擁有這個(gè)指針并且能在析構(gòu)時(shí)自動(dòng)刪除這個(gè)指針?biāo)傅膶?duì)象。因?yàn)檫@個(gè)智能指針可以簡(jiǎn)單的當(dāng)成一個(gè)自動(dòng)的對(duì)象(這就是說,它出了作用域時(shí)會(huì)自動(dòng)毀滅),所以很自然的把它稱之為“智能”指針:
<textarea cols="50" rows="15" name="code" class="cpp">// 示例1(b):安全代碼,使用了auto_ptr void f() { auto_ptr<T> pt( new T ); /*...更多的代碼...*/ } // 酷:當(dāng)pt出了作用域時(shí)析構(gòu)函數(shù)被調(diào)用,從而對(duì)象被自動(dòng)刪除</textarea>
現(xiàn)在代碼不會(huì)泄漏T類型的對(duì)象,不管這個(gè)函數(shù)是正常退出還是拋出了異常,因?yàn)閜t的析構(gòu)函數(shù)總是會(huì)在出棧時(shí)被調(diào)用,清理會(huì)自動(dòng)進(jìn)行。
最后,使用一個(gè)auto_ptr就像使用一個(gè)內(nèi)建的指針一樣容易,而且如果想要“撤銷”資源,重新采用手動(dòng)的所有權(quán),我們只要調(diào)用release()。
<textarea cols="50" rows="15" name="code" class="cpp">// 示例2:使用一個(gè)auto_ptr void g() { // 現(xiàn)在,我們有了一個(gè)分配好的對(duì)象 T* pt1 = new T; // 將所有權(quán)傳給了一個(gè)auto_ptr對(duì)象 auto_ptr<T> pt2(pt1); // 使用auto_ptr就像我們以前使用簡(jiǎn)單指針一樣, *pt2 = 12; // 就像*pt1 = 12 pt2->SomeFunc(); // 就像pt1->SomeFunc(); // 用get()來獲得指針的值 assert( pt1 == pt2.get() ); // 用release()來撤銷所有權(quán) T* pt3 = pt2.release(); // 自己刪除這個(gè)對(duì)象,因?yàn)楝F(xiàn)在沒有任何auto_ptr擁有這個(gè)對(duì)象 delete pt3; } // pt2不再擁有任何指針,所以不要試圖刪除它...OK,不要重復(fù)刪除 </textarea>
最后,我們可以使用auto_ptr的reset()函數(shù)來重置auto_ptr使之擁有另一個(gè)對(duì)象。如果這個(gè)auto_ptr已經(jīng)擁有了一個(gè)對(duì)象,那么,它會(huì)先刪除已經(jīng)擁有的對(duì)象,因此調(diào)用reset()就如同銷毀這個(gè)auto_ptr,然后新建一個(gè)并擁有一個(gè)新對(duì)象:
<textarea cols="50" rows="15" name="code" class="cpp">// 示例 3:使用reset() void h() { auto_ptr<T> pt( new T(1) ); pt.reset( new T(2) ); // 刪除由"new T(1)"分配出來的第一個(gè)T } // 最后pt出了作用域,第二個(gè)T也被刪除了</textarea>
auto_ptr用法:
1. 需要包含頭文件<memory>。
2. Constructor:explicit auto_ptr(X* p = 0) throw(); 將指針p交給auto_ptr對(duì)象托管。
3. Copy constructor:auto_ptr(const auto_ptr&) throw(); template<class Y> auto_ptr(const auto_ptr<Y>& a) throw(); 指針的托管權(quán)會(huì)發(fā)生轉(zhuǎn)移。
4. Destructor: ~auto_ptr(); 釋放指針p指向的空間。
5. 提供了兩個(gè)成員函數(shù) X* get() const throw(); //返回保存的指針
6. 對(duì)象中仍保留指針 X* release() const throw(); //返回保存的指針,對(duì)象中不保留指針
auto_ptr實(shí)現(xiàn)關(guān)鍵點(diǎn):
1. 利用特點(diǎn)“棧上對(duì)象在離開作用范圍時(shí)會(huì)自動(dòng)析構(gòu)”。
2. 對(duì)于動(dòng)態(tài)分配的內(nèi)存,其作用范圍是程序員手動(dòng)控制的,這給程序員帶來了方便但也不可避免疏忽造成的內(nèi)存泄漏,畢竟只有編譯器是最可靠的。
3. auto_ptr通過在棧上構(gòu)建一個(gè)對(duì)象a,對(duì)象a中wrap了動(dòng)態(tài)分配內(nèi)存的指針p,所有對(duì)指針p的操作都轉(zhuǎn)為對(duì)對(duì)象a的操作。而在a的析構(gòu)函數(shù)中會(huì)自動(dòng)釋放p的空間,而該析構(gòu)函數(shù)是編譯器自動(dòng)調(diào)用的,無(wú)需程序員操心。
多說無(wú)益,看一個(gè)最實(shí)用的例子:
<textarea cols="50" rows="15" name="code" class="cpp">#include <iostream> #include <memory> using namespace std; class TC { public: TC(){cout<<"TC()"<<endl;} ~TC(){cout<<"~TC()"<<endl;} }; void foo(bool isThrow) { auto_ptr<TC> pTC(new TC); // 方法2 //TC *pTC = new TC; // 方法1 try { if(isThrow) throw "haha"; } catch(const char* e) { //delete pTC; // 方法1 throw; } //delete pTC; // 方法1 } int main() { try { foo(true); } catch(...) { cout<<"caught"<<endl; } system("pause"); }</textarea>
1. 如果采用方案1,那么必須考慮到函數(shù)在因throw異常的時(shí)候釋放所分配的內(nèi)存,這樣造成的結(jié)果是在每個(gè)分支處都要很小心的手動(dòng) delete pTC;。
2. 如果采用方案2,那就無(wú)需操心何時(shí)釋放內(nèi)存,不管foo()因何原因退出, 棧上對(duì)象pTC的析構(gòu)函數(shù)都將調(diào)用,因此托管在之中的指針?biāo)傅膬?nèi)存必然安全釋放。
至此,智能指針的優(yōu)點(diǎn)已經(jīng)很明了了。
但是要注意使用中的一個(gè)陷阱,那就是指針的托管權(quán)是會(huì)轉(zhuǎn)移的。 例如在上例中,如果 auto_ptr<TC> pTC(new TC); auto_ptr<TC> pTC1=pTC; 那么,pTC1將擁有該指針,而pTC沒有了,如果再用pTC去引用,必然導(dǎo)致內(nèi)存錯(cuò)誤。
要避免這個(gè)問題,可以考慮使用采用了引用計(jì)數(shù)的智能指針,例如boost::shared_ptr等。auto_ptr不會(huì)降低程序的效率,但auto_ptr不適用于數(shù)組,auto_ptr根本不可以大規(guī)模使用。 shared_ptr也要配合weaked_ptr,否則會(huì)很容易觸發(fā)循環(huán)引用而永遠(yuǎn)無(wú)法回收內(nèi)存。 理論上,合理使用容器加智能指針,C++可以完全避免內(nèi)存泄露,效率只有微不足道的下降(中型以上程序最多百分之一)。
以上所述是小編給大家介紹的C++中auto_ptr智能指針的用法詳解,希望對(duì)大家有所幫助,如果大家有任何疑問請(qǐng)給我留言,小編會(huì)及時(shí)回復(fù)大家的。在此也非常感謝大家對(duì)我們網(wǎng)站的支持!
上一篇:C語(yǔ)言二進(jìn)制思想以及數(shù)據(jù)的存儲(chǔ)
欄 目:C語(yǔ)言
下一篇:C 語(yǔ)言簡(jiǎn)單加減乘除運(yùn)算
本文標(biāo)題:C++中auto_ptr智能指針的用法詳解
本文地址:http://mengdiqiu.com.cn/a1/Cyuyan/2170.html
您可能感興趣的文章
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
- 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)數(shù)怎么表達(dá)
- 04-02c語(yǔ)言沒有round函數(shù) round c語(yǔ)言
- 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎么打出三角函數(shù)的值
- 01-10深入理解C++中常見的關(guān)鍵字含義
- 01-10使用C++實(shí)現(xiàn)全排列算法的方法詳解
- 01-10深入Main函數(shù)中的參數(shù)argc,argv的使用詳解
- 01-10APUE筆記之:進(jìn)程環(huán)境詳解
- 01-10c++中inline的用法分析
- 01-10如何尋找數(shù)組中的第二大數(shù)


閱讀排行
- 1C語(yǔ)言 while語(yǔ)句的用法詳解
- 2java 實(shí)現(xiàn)簡(jiǎn)單圣誕樹的示例代碼(圣誕
- 3利用C語(yǔ)言實(shí)現(xiàn)“百馬百擔(dān)”問題方法
- 4C語(yǔ)言中計(jì)算正弦的相關(guān)函數(shù)總結(jié)
- 5c語(yǔ)言計(jì)算三角形面積代碼
- 6什么是 WSH(腳本宿主)的詳細(xì)解釋
- 7C++ 中隨機(jī)函數(shù)random函數(shù)的使用方法
- 8正則表達(dá)式匹配各種特殊字符
- 9C語(yǔ)言十進(jìn)制轉(zhuǎn)二進(jìn)制代碼實(shí)例
- 10C語(yǔ)言查找數(shù)組里數(shù)字重復(fù)次數(shù)的方法
本欄相關(guān)
- 04-02c語(yǔ)言函數(shù)調(diào)用后清空內(nèi)存 c語(yǔ)言調(diào)用
- 04-02func函數(shù)+在C語(yǔ)言 func函數(shù)在c語(yǔ)言中
- 04-02c語(yǔ)言的正則匹配函數(shù) c語(yǔ)言正則表達(dá)
- 04-02c語(yǔ)言用函數(shù)寫分段 用c語(yǔ)言表示分段
- 04-02c語(yǔ)言中對(duì)數(shù)函數(shù)的表達(dá)式 c語(yǔ)言中對(duì)
- 04-02c語(yǔ)言編寫函數(shù)冒泡排序 c語(yǔ)言冒泡排
- 04-02c語(yǔ)言沒有round函數(shù) round c語(yǔ)言
- 04-02c語(yǔ)言分段函數(shù)怎么求 用c語(yǔ)言求分段
- 04-02C語(yǔ)言中怎么打出三角函數(shù) c語(yǔ)言中怎
- 04-02c語(yǔ)言調(diào)用函數(shù)求fibo C語(yǔ)言調(diào)用函數(shù)求
隨機(jī)閱讀
- 01-10使用C語(yǔ)言求解撲克牌的順子及n個(gè)骰子
- 04-02jquery與jsp,用jquery
- 08-05DEDE織夢(mèng)data目錄下的sessions文件夾有什
- 01-10SublimeText編譯C開發(fā)環(huán)境設(shè)置
- 01-10C#中split用法實(shí)例總結(jié)
- 01-11Mac OSX 打開原生自帶讀寫NTFS功能(圖文
- 01-11ajax實(shí)現(xiàn)頁(yè)面的局部加載
- 08-05織夢(mèng)dedecms什么時(shí)候用欄目交叉功能?
- 01-10delphi制作wav文件的方法
- 08-05dedecms(織夢(mèng))副欄目數(shù)量限制代碼修改