C++中的智能指針

1.auto_ptr,share_ptr概述

在開發過程中,曾經使用過兩種C++的智能指針:auto_ptr和shared_ptr如今,便總結一下,順便比較比較二者使用中的區別,注意避免入坑的危險。
我們知道,在C++中,如果創建一個指向某個對象的指針,那么在使用完這個對象之后我們需要自己delete它。否則,會造成一個懸垂指針(dangling pointer),非常容易造成內存泄漏。事實上,如果在使用指針的同時,程序運行拋出異常,那么所指向的對象仍然不會被安全刪除,依然可能會出現內存泄露的危險。與普通的指針相比而言,智能指針的區別在于它實際上是對普通指針加了一層封裝機制,這樣的一層封裝機制的目的是使智能指針可以方便的管理一個對象的生命期。在生命期結束的時候,自動的刪除內存中被占用的區域。也就是說,智能指針的出現實際上就是為了可以方便的控制對象的生命期,在智能指針中,一個對象什么時候和在什么條件下要被析構或者是刪除是受智能指針本身決定的,用戶并不需要管理,以此來增強程序的健壯性。

A.auto_ptr

它是C++標準庫提供的一種智能指針,在構造時獲取某個對象的所有權,在析構時,釋放該對象RAII(Resource Acquisition Is Initialization),也稱為“資源獲取就是初始化”,是C++語言的一種管理資源、避免泄漏的慣用法。C++標準保證任何情況下,已構造的對象最終會銷毀,即它的析構函數最終會被調用。
簡單的說,RAII的做法是使用一個對象,在其構造時獲取資源,在對象生命期控制對資源的訪問使之始終保持有效,最后在對象析構的時候釋放資源。
如下例所示,即為auto_ptr的使用規則:

int *p = new int(10);  
std::auto_ptr <int> ap(p);  

但是使用auto_ptr,有一些必須注意的問題:

  • (1)不可以將兩個或兩個以上的auto_ptr指向同一個對象,因為其中當其中一個auto_ptr的生命期結束時,它就會析構對象,而另一個auto_ptr在析構時,將造成多重析構問題,這是很不安全的內存操作。故而,以下的行為是一定要避免的:
int  *p = new int(0);  
auto_ptr<int> ap1(p);  
auto_ptr<int> ap2(p);  
//ap1和ap2都指向一個對象,這就是極危險的,必須防止這么使用
  • (2)不可使用auto_ptr來管理數組指針
    因為auto_ptr在析構時,使用的是delete,而不是delete[]。所以以下的使用方式是絕對要避免的。
int *pa = new int[10];  
auto_ptr<int> ap(pa);  
//這種寫法是完全錯誤的,極可能引起內存泄露的危險
  • (3)
    auto_ptr強調對“裸”指針的完全占有性。也就是說,一個“裸”指針不能同時被兩個以上的“裸”指針所擁有。那么,在拷貝構造函數和賦值中,auto_ptr均采取了“所有權轉移”的策略,即原指針將失去對裸指針的所有權。
int *p = new int (10);  
auto_ptr<int> ap1(p);  
auto_ptr<int> ap2 = ap1;  //這會使得ap1變成了NULL
cout << *ap1 <<endl;      //錯誤!此時,對其解引用是不安全的。

如上的錯誤相對而言,比較容易避免,但是對于以下的代碼,錯誤就較難發現。

void fun(auto_ptr<int> ap)  
{  
     cout << *ap << endl;  
}  
auto_ptr<int> ap(new int(0) ) ;  
fun(ap);  
cout << *ap << endl;  
//錯誤!對空指針的解引用

上述情況比較隱蔽,這是因為在函數調用時,傳參引起了拷貝構造函數的調用,所以原來的指針失去了其所有權。

  • (4)auto_ptr不具備值語義,所以auto_ptr不能被用在STL容器中。
    所謂值語義:使之符合以下條件的類型,設有類Obj:
Obj a;  
Obj b(a);  
Obj c;  
c = a;  

那么a== b, a == c顯然,auto_ptr并不具有這個特點。

  • auto_ptr用法要點:
    A. 需要包含頭文件<memory>
    B.Constructor:explicit auto_ptr(X* p = 0) throw(); 將指針p交給auto_ptr對象托管。
    C.Copy constructor:auto_ptr(constauto_ptr&) throw(); template<class Y> auto_ptr(constauto_ptr<Y>& a) throw(); 指針的托管權會發生轉移。
    D.Destructor: ~auto_ptr(); 釋放指針p指向的空間。
    E. 提供了兩個成員函數 Xget() const throw(); //返回保存的指針
    F. 對象中仍保留指針 X
    release() const throw(); //返回保存的指針,對象中不保留指針

  • auto_ptr實現關鍵點:
    A.利用特點“棧上對象在離開作用范圍時會自動析構”。
    B.對于動態分配的內存,其作用范圍是程序員手動控制的,這給程序員帶來了方便但也不可避免疏忽造成的內存泄漏,畢竟只有編譯器是最可靠的。
    C.auto_ptr通過在棧上構建一個對象a,對象a中wrap了動態分配內存的指針p,所有對指針p的操作都轉為對對象a的操作。而在a的析構函數中會自動釋放p的空間,而該析構函數是編譯器自動調用的,無需程序員操心。

B.shared_ptr

鑒于auto_ptr所出現的無法復制,且不能滿足標準容器對元素的要求,所以boost庫中提供了一種新型的智能指針shared_ptr,它通過引用計數(reference counting)的原理,解決了多個指針之間共享對象所有權的問題,可以被自由地拷貝和賦值,在任意的地方共享它,當沒有代碼使用(引用計數為0)它時才刪除被包裝的動態分配的對象。 shared_ptr也可以安全地放到標準容器中,并彌補了auto_ptr因為轉移語義而不能把指針作為STL容器元素的缺陷。

在C++11中,shared_ptr在<memory>中被定義為: template<class T > class shared_ptr;
std::shared_ptr是通過指針保持某個對象的共享擁有權的智能指針。若干個shared_ptr對象可以擁有同一個對象;最后一個指向該對象的shared_ptr被銷毀或重置時,該對象被銷毀。銷毀該對象時使用的是delete表達式或者是在構造shared_ptr時傳入的自定義刪除器(deleter)。
shared_ptr也可以不擁有對象,稱作空(empt))。 shared_ptr滿足CopyConstructible和CopyAssignable的要求。

  • shared_ptr實現細節
成員類型 定義
element_type T The type of the managed object 即被shared_ptr管理著的對象
  • 包含的成員函數如下所示:
成員函數 成員函數
(constructor) constructsnew shared_ptr
(destructor) 如果沒有更多shared_ptrs的的鏈接解構擁有的對象
operator= 分配shared_ptr
reset 取代管理的對象
swap 交換所管理的對象
get 返回一個指針,指向被管理對象
operator* /operator-> 解引用指針到的管理對象
use_count shared_ptr對象指的是在同一個管理對象的數量
unique 檢查是否被管理對象的管理僅由當前shared_ptr的實例
operator bool 檢查是否有相關的管理對象
owner_before Owner-based ordering

智能指針share_ptr的實現基于引用計數實現,有機會分析一個引用計數的原理。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,406評論 6 538
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,034評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,413評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,449評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,165評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,559評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,606評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,781評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,327評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,084評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,278評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,849評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,495評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,927評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,172評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,010評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,241評論 2 375

推薦閱讀更多精彩內容

  • 原作者:Babu_Abdulsalam 本文翻譯自CodeProject,轉載請注明出處。 引入### Ooops...
    卡巴拉的樹閱讀 30,134評論 13 74
  • 導讀## 最近在補看《C++ Primer Plus》第六版,這的確是本好書,其中關于智能指針的章節解析的非常清晰...
    小敏紙閱讀 2,010評論 1 12
  • 1. 什么是智能指針? 智能指針是行為類似于指針的類對象,但這種對象還有其他功能。 2. 為什么設計智能指針? 引...
    MinoyJet閱讀 645評論 0 1
  • 內存泄露(臭名昭著的BUG)動態申請堆空間,用完后不歸還C++語言中沒有垃圾回收的機制指針無法控制所指堆空間的生命...
    nethanhan閱讀 323評論 0 0