C++智能指針原理與簡單實現

Java面試官經常喜歡問關于垃圾回收的問題。而他最終給出的答案往往是:給對象中添加一個引用計數器,每當有一個地方引用它時,計算器值就加1;當引用失效時,計數器值就減1;任何時候計數器為0的對象就是不可能再被使用的。

客觀的說,引用計數算法的實現簡單,判定效率也很高,在大部分情況下它都是一個不錯的算法。但是,至少主流的Java虛擬機里面沒有選用引用計數算法來管理內存,其中最主要的原因是它很難解決對象之間的相互循環引用的問題。在主流的商用程序語言(Java、C#)的主流實現中通過可達性分析來判定對象存活的。

這里,我們不去談可達性分析策略。至少,引用計數算法還是有一定的用武之地的。比如說,蘋果為自家的ios開發編程語言objective-c引用了ARC機制來進行內存管理,在很大程度上消除了手動內存管理的負擔。為了避免對象之間循環引用,我們可以將對象聲明為弱引用。而在C++中,內存的分配和釋放需要手動來管理,這在一定程度上帶來了內存泄漏的隱患。幸運的是,C++標準庫中提供了一種叫做智能指針(shared_ptrs)的類,智能指針的作用有如同指針,但會記錄有多少個shared_ptrs共同指向一個對象。這便是所謂的引用計數。一旦最后一個這樣的指針被銷毀,也就是一旦某個對象的引用計數變為0,這個對象會被自動刪除。

比如說,用智能指針來創建一個動態分配的字符串對象:

//新創建一個對象,引用計數器為1
shared_ptr<string> pstr(new string("abc"));

解引用一個智能指針返回它指向的對象。同樣,我們可以像操作普通指針一樣調用string提供的方法。

if (pstr && pstr->empty()) {
        *pstr = "hello";
}

當有另外一個智能指針對當前智能指針進行拷貝時,引用計數器加1:

shared_ptr<string> pstr(new string("abc")); //pstr指向的對象只有一個引用者
shared_ptr<string> pstr2(pstr); //pstr跟pstr2指向相同的對象,此對象有兩個引用者

當兩個智能指針進行賦值操作時,左邊的指針指向的對象引用計數減1,右邊的加1。

shared_ptr<string> pstr(new string("abc"));
shared_ptr<string> pstr2(new string("hello"));
pstr2 = pstr; //給pstr2賦值,令他指向另一個地址,遞增pstr指向的對象的引用計數,遞減pstr2原來指向的對象引用計數

指針離開作用域范圍時,同樣引用計數減1。當引用計數為0時,對象被回收。

根據以上的分析,我們對它做一個簡單的實現:

template <typename T>
class smart_ptrs {

public:
    smart_ptrs(T*); //用普通指針初始化智能指針
    smart_ptrs(smart_ptrs&);

    T* operator->(); //自定義指針運算符
    T& operator*(); //自定義解引用運算符
    smart_ptrs& operator=(smart_ptrs&); //自定義賦值運算符
    
    ~smart_ptrs(); //自定義析構函數

private:
    int *count; //引用計數
    T *p; //智能指針底層保管的指針
};

跟標準庫一樣,我們使用模板來實現它。
用普通指針進行初始化時,需要將該指針進行封裝,并且引用計數初始化為1。

template <typename T>
smart_ptrs<T>::smart_ptrs(T *p): count(new int(1)), p(p) {
}

定義拷貝構造函數:

template <typename T>
//對普通指針進行拷貝,同時引用計數器加1,因為需要對參數進行修改,所以沒有將參數聲明為const
smart_ptrs<T>::smart_ptrs(smart_ptrs &sp): count(&(++*sp.count)), p(sp.p)  {
}

定義指針運算符:

template <typename T>
 T* smart_ptrs<T>::operator->() {
    return p;
 }

定義解引用運算符,直接返回底層指針的引用:

template <typename T>
T& smart_ptrs<T>::operator*() {
    return *p;
}

定義賦值運算符,左邊的指針計數減1,右邊指針計數加1,當左邊指針計數為0時,釋放內存:

template <typename T>
smart_ptrs<T>& smart_ptrs<T>::operator=(smart_ptrs& sp) {
    ++*sp.count;
    if (--*count == 0) { //自我賦值同樣能保持正確
        delete count;
        delete p;
    }
    this->p = sp.p;
    this->count = sp.count;
    return *this;
}

定義析構函數:

template <typename T>
smart_ptrs<T>::~smart_ptrs() {
    if (--*count == 0) {
        delete count;
        delete p;
    }
}

好了,大功告成!接下來,我們用這段代碼進行測試:

smart_ptrs<string> pstr(new string("abc"));
smart_ptrs<string> pstr2(pstr);
smart_ptrs<string> pstr3(new string("bcd"));
pstr3 = pstr2;

為了讓測試結果更明顯,我在方法中加入了一些輸出,測試結果如下:

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

推薦閱讀更多精彩內容

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,532評論 1 51
  • 從三月份找實習到現在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發崗...
    時芥藍閱讀 42,325評論 11 349
  • 導讀## 最近在補看《C++ Primer Plus》第六版,這的確是本好書,其中關于智能指針的章節解析的非常清晰...
    小敏紙閱讀 2,010評論 1 12
  • 1.1 概述 Java優點: 1、結構嚴謹,面向對象 2、擺脫硬件平臺束縛,實現了“一次編寫,到處運行”的理想; ...
    viciyforever閱讀 1,200評論 1 9
  • 不知是偶然還是巧然,在夏末繽紛的季節里,因眼角的余溫,不小心便被騰訊的首頁推薦《明日之子》給帶走,又因剛剛好安安巧...
    芳草幽蘭閱讀 286評論 3 1