weak_ptr作用

weak_ptr的用處_weak_ptr的作用_kyowill的博客-CSDN博客

循環(huán)引用問題

#include <memory>

class B; // 前向聲明

class A {
public:
    std::shared_ptr<B> b_ptr;
};

class B {
public:
    std::shared_ptr<A> a_ptr;
};

int main() {
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();

    a->b_ptr = b;
    b->a_ptr = a;

    return 0;
}

循環(huán)引用示意圖

見上圖,當 main 函數(shù)結(jié)束時塔粒,a 和 b 這兩個共享指針會被銷毀,但是因為 a->b_ptr 和 b->a_ptr 的共享指針引用仍然存在,所以 A 和 B 對象共享指針引用計數(shù)都從2變?yōu)?静袖,于是這兩個對象都沒有正常銷毀

為了解決循環(huán)引用的問題房交,可以使用 std::weak_ptr盅惜,它可以避免形成循環(huán)引用徐绑,因為** std::weak_ptr 不會增加引用計數(shù)**邪驮。只有當需要使用對象時,可以將 std::weak_ptr 轉(zhuǎn)換成 std::shared_ptr傲茄,如果對象已經(jīng)被銷毀耕捞,則轉(zhuǎn)換會失敗。

以下是使用 std::weak_ptr 改進上述代碼的示例:

#include <memory>

class B; // Forward declaration

class A {
public:
    std::shared_ptr<B> b_ptr;
};

class B {
public:
    std::weak_ptr<A> a_ptr; // Using weak_ptr here
};

int main() {
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();

    a->b_ptr = b;
    b->a_ptr = a;

    return 0;
}

這樣烫幕,AB 之間的循環(huán)引用就不會造成內(nèi)存泄漏。

具體說明 std::weak_ptr 轉(zhuǎn)換成 std::shared_ptr然后使用敞映?

當你需要使用 std::weak_ptr 指向的對象時较曼,你可以通過調(diào)用 std::weak_ptrlock() 成員函數(shù)來嘗試將它轉(zhuǎn)換為一個有效的 std::shared_ptr。這個操作會檢查所指向的對象是否還存在振愿,如果存在捷犹,就會返回一個指向該對象的有效 std::shared_ptr,否則返回一個空的 std::shared_ptr冕末。

在你的代碼中萍歉,如果你需要使用 B 對象所指向的 A 對象,你可以這樣做:

#include <memory>

class B; // Forward declaration

class A {
public:
    std::shared_ptr<B> b_ptr;
};

class B {
public:
    std::weak_ptr<A> a_ptr; // Using weak_ptr here
};

int main() {
    std::shared_ptr<A> a = std::make_shared<A>();
    std::shared_ptr<B> b = std::make_shared<B>();

    a->b_ptr = b;
    b->a_ptr = a;

    // Using weak_ptr to access the object
    std::shared_ptr<A> a_from_b = b->a_ptr.lock();
    if (a_from_b) {
        // You can safely use a_from_b here
    } else {
        // The object has been destroyed
    }

    return 0;
}

在上面的代碼中档桃,通過調(diào)用 b->a_ptr.lock()枪孩,你可以獲得一個指向 A 對象的 std::shared_ptr,如果 A 對象仍然存在的話藻肄。如果 A 對象已經(jīng)被銷毀蔑舞,a_from_b 將會是一個空的 std::shared_ptr。這樣你就可以在使用對象之前檢查它是否還存在嘹屯,避免懸空指針的問題攻询。

注意

  • 雖然通過弱引用指針可以有效的解除循環(huán)引用,但這種方式必須在能預見會出現(xiàn)循環(huán)引用的情況下才能使用州弟,即這個僅僅是一種編譯期的解決方案钧栖,如果程序在運行過程中出現(xiàn)了循環(huán)引用低零,還是會造成內(nèi)存泄漏的。因此拯杠,不要認為只要使用了智能指針便能杜絕內(nèi)存泄漏掏婶。

具體說明

weak_ptr是為配合shared_ptr而引入的一種智能指針,它更像是shared_ptr的一個助手阴挣,而不是一個智能指針气堕。它的最大作用在于協(xié)助shared_ptr工作,像旁觀者那樣觀測資源的使用情況畔咧。weak_ptr被設(shè)計為與shared_ptr協(xié)同工作茎芭,可以從一個shared_ptr或者另外一個weak_ptr對象構(gòu)造,獲得資源的觀測權(quán)限誓沸。但weak_ptr沒有共享資源梅桩,它的構(gòu)造不會引入指針引用技術(shù)的增加,同樣拜隧,weak_ptr析構(gòu)時也不會導致引用技術(shù)減少宿百,它只是一個靜靜的觀察著。weak_ptr的一個很重要作用是:打破循環(huán)引用洪添。

讓我們一步步來分析循環(huán)引用問題垦页。

  1. SharedPtrNode
#ifndef SHAREPTRNODE_H_
#define SHAREPTRNODE_H_

#include <iostream>
using  namespace  std;

#include <boost/smart_ptr.hpp>
using  namespace  boost;

class  SharePtrNode {
public :
   int   spNodeId;
   SharePtrNode();
   SharePtrNode( int  nId);
   virtual  ~SharePtrNode();

   typedef  boost::shared_ptr<SharePtrNode> ptr_type;
   ptr_type next;
};

#endif /* SHAREPTRNODE_H_ */
    
#include "SharePtrNode.h"
#include <iomanip>  //for setw and setfill
 
SharePtrNode::SharePtrNode():SharePtrNode(0) {
     // TODO Auto-generated constructor stub
 
}
 
SharePtrNode::SharePtrNode( int  nId)
{
     spNodeId = nId;
     cout <<  "SharePtrNode, [spNodeId:" 
                     << setw(4) << setfill( '0' ) << spNodeId <<  "]"  << endl;
}
 
SharePtrNode::~SharePtrNode() {
     // TODO Auto-generated destructor stub
     cout <<  "~SharePtrNode, [spNodeId:" 
                     << setw(4) << setfill( '0' ) << spNodeId <<  "]"  << endl;
}

一個SharePtrNode對象包含一個自身id(spNodeId)和一個指針:ptr_type next。這個指針是一個shared_ptr類型的指針干奢∪福基于上述SharePtrNode類,我們構(gòu)建一個簡單的使用場景:動態(tài)創(chuàng)建兩個SharePtrNode對象忿峻。

void  Case03_ShareAndWeakPtrLoopRef_1()
{
     auto  sharedPtrNode1 = boost::make_shared<SharePtrNode>(1);
     auto  sharedPtrNode2 = boost::make_shared<SharePtrNode>(2);
}

由于是通過shared_ptr管理的兩個對象薄啥,因此,兩個對象會在函數(shù)退出時自動銷毀逛尚,且銷毀的順序與創(chuàng)建的順序相反垄惧。


——> Case03_ShareAndWeakPtrLoopRef_1

SharePtrNode, [spNodeId:0001]

SharePtrNode, [spNodeId:0002]

~SharePtrNode, [spNodeId:0002]

~SharePtrNode, [spNodeId:0001]

這種場景下一切似乎都非常合理。
引用示意圖如下:


那如果對case場景做進一步補充:將sharedPtrNode1的next指針指向sharedPtrNode2绰寞,會產(chǎn)生什么結(jié)果呢到逊?

    
void  Case03_ShareAndWeakPtrLoopRef_2()
{
     auto  sharedPtrNode1 = boost::make_shared<SharePtrNode>(1);
     auto  sharedPtrNode2 = boost::make_shared<SharePtrNode>(2);
 
         sharedPtrNode1->next = sharedPtrNode2;
}

這個時候,會有什么輸出出現(xiàn)呢克握?


——> Case03_ShareAndWeakPtrLoopRef_2

SharePtrNode, [spNodeId:0001]

SharePtrNode, [spNodeId:0002]

~SharePtrNode, [spNodeId:0001]

~SharePtrNode, [spNodeId:0002]

順序變了蕾管,為什么會這樣?因為在執(zhí)行sharedPtrNode1->next = sharedPtrNode2;的時候?qū)嶋H上SharedPtrNode2的引用計數(shù)已經(jīng)從1增加到了2菩暗,當函數(shù)退出時掰曾,首先SharedPtrNode2先析構(gòu),此時SharedPtrNode2的引用技術(shù)從2減回到1停团,因為不到0旷坦,因此實際的SharedPtrNode2此時還不能銷毀掏熬,析構(gòu)函數(shù)沒有被調(diào)用。而此后SharedPtrNode1進行析構(gòu)秒梅,此時因為SharedPtrNode1的引用計數(shù)原本只有1旗芬,此時降低到0,因此SharedPtrNode1的析構(gòu)函數(shù)被調(diào)用:“~SharePtrNode, [spNodeId:0001]”捆蜀,在SharedPtrNode1進行析構(gòu)的時候疮丛,SharedPtrNode1的成員變量sharedPtrNode1->next也會一并析構(gòu),此時由于next指向的節(jié)點引用計數(shù)已經(jīng)是1了辆它,析構(gòu)的時候誊薄,變成0,因此執(zhí)行最終指向節(jié)點的SharePtrNode2的析構(gòu)锰茉,因此“~SharePtrNode, [spNodeId:0002]”輸出呢蔫。這也就是為什么node2在node1之后析構(gòu)的原因。
引用示意圖如下:


  • 共享指針就是用來管理對象的生命周期的飒筑,對象的是否析構(gòu)要看指向該對象的共享指針的引用計數(shù)是否降為0片吊,為0則析構(gòu),否則不析構(gòu)协屡。(注意區(qū)分共享指針對象的析構(gòu)≠共享指針指向?qū)ο蟮奈鰳?gòu)
  • 析構(gòu)時俏脊,指向B的共享指針對象b先析構(gòu),此時指向B的共享指針引用計數(shù)由2變?yōu)?肤晓,但不是0联予,所以B對象的析構(gòu)函數(shù)未被調(diào)用
  • 而后,指向A的共享指針對象a先析構(gòu)材原,此時指向A的共享指針引用計數(shù)由1變?yōu)?,于是A的析構(gòu)函數(shù)被調(diào)用
  • 由于對象A的析構(gòu)季眷,其成員——指向B對象的共享指針對象a被析構(gòu)余蟹,此時B的共享指針引用計數(shù)由1變?yōu)?,這時B的析構(gòu)函數(shù)被調(diào)用
  • 這也就解釋了上述的析構(gòu)函數(shù)調(diào)用順序問題

在進一步構(gòu)建一個更為復雜一點的場景:再sharedPtrNode2的next指針指向sharedPtrNode1子刮,會產(chǎn)生什么結(jié)果呢威酒?

void  Case03_ShareAndWeakPtrLoopRef_3()
{
     auto  sharedPtrNode1 = boost::make_shared<SharePtrNode>(1);
     auto  sharedPtrNode2 = boost::make_shared<SharePtrNode>(2);
 
         sharedPtrNode1->next = sharedPtrNode2;
         sharedPtrNode2->next = sharedPtrNode1;
}

我們可以看到,此時的輸出結(jié)果如下:


——> Case03_ShareAndWeakPtrLoopRef_3

SharePtrNode, [spNodeId:0001]

SharePtrNode, [spNodeId:0002]

兩個對象都值進行了初始化挺峡,而沒有進行析構(gòu)葵孤!
這里可參見開頭的解釋。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末橱赠,一起剝皮案震驚了整個濱河市尤仍,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌狭姨,老刑警劉巖宰啦,帶你破解...
    沈念sama閱讀 219,366評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件苏遥,死亡現(xiàn)場離奇詭異,居然都是意外死亡赡模,警方通過查閱死者的電腦和手機田炭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,521評論 3 395
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來漓柑,“玉大人教硫,你說我怎么就攤上這事×静迹” “怎么了瞬矩?”我有些...
    開封第一講書人閱讀 165,689評論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長谚殊。 經(jīng)常有香客問我丧鸯,道長,這世上最難降的妖魔是什么嫩絮? 我笑而不...
    開封第一講書人閱讀 58,925評論 1 295
  • 正文 為了忘掉前任丛肢,我火速辦了婚禮,結(jié)果婚禮上剿干,老公的妹妹穿的比我還像新娘蜂怎。我一直安慰自己,他們只是感情好置尔,可當我...
    茶點故事閱讀 67,942評論 6 392
  • 文/花漫 我一把揭開白布杠步。 她就那樣靜靜地躺著,像睡著了一般榜轿。 火紅的嫁衣襯著肌膚如雪幽歼。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,727評論 1 305
  • 那天谬盐,我揣著相機與錄音甸私,去河邊找鬼。 笑死飞傀,一個胖子當著我的面吹牛皇型,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播砸烦,決...
    沈念sama閱讀 40,447評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼弃鸦,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了幢痘?” 一聲冷哼從身側(cè)響起唬格,我...
    開封第一講書人閱讀 39,349評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后西轩,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體员舵,經(jīng)...
    沈念sama閱讀 45,820評論 1 317
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,990評論 3 337
  • 正文 我和宋清朗相戀三年藕畔,在試婚紗的時候發(fā)現(xiàn)自己被綠了马僻。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,127評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡注服,死狀恐怖韭邓,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情溶弟,我是刑警寧澤女淑,帶...
    沈念sama閱讀 35,812評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站辜御,受9級特大地震影響鸭你,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜擒权,卻給世界環(huán)境...
    茶點故事閱讀 41,471評論 3 331
  • 文/蒙蒙 一袱巨、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧碳抄,春花似錦愉老、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,017評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至璧尸,卻和暖如春咒林,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背爷光。 一陣腳步聲響...
    開封第一講書人閱讀 33,142評論 1 272
  • 我被黑心中介騙來泰國打工映九, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人瞎颗。 一個月前我還...
    沈念sama閱讀 48,388評論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像捌议,于是被迫代替她去往敵國和親哼拔。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,066評論 2 355

推薦閱讀更多精彩內(nèi)容