1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

        一文徹底掌握智能指針

        共 989字,需瀏覽 2分鐘

         ·

        2022-04-16 13:38

        前言:

        • 大家好,今天繼續(xù)分享一篇基礎(chǔ)的智能指針的使用,在分享這篇之前,大家可以看之前分享的兩種智能指針:C++智能指針學(xué)習(xí)(一), 今天我們來(lái)分享剩下的兩個(gè)智能指針:

        • std::share_ptr

        • std::weak_ptr

        • 在分享之前,簡(jiǎn)單回憶一下智能指針的概念:

        • 智能指針:它是存儲(chǔ)指向動(dòng)態(tài)分配(堆)對(duì)象指針的類,用于生存期控制,能夠確保在離開指針?biāo)谧饔糜驎r(shí),自動(dòng)正確的銷毀動(dòng)態(tài)內(nèi)存分配的對(duì)象,防止內(nèi)存泄漏。

        std::shared_ptr:

        std::unique_ptr 對(duì)其持有的資源具有獨(dú)占性,而 std::shared_ptr 持有的資源可以在多個(gè) std::shared_ptr 之間共享,每多一個(gè) std::shared_ptr 對(duì)資源的引用,資源引用計(jì)數(shù)將增加 1,每一個(gè)指向該資源的 std::shared_ptr 對(duì)象析構(gòu)時(shí),資源引用計(jì)數(shù)減 1,最后一個(gè) std::shared_ptr 對(duì)象析構(gòu)時(shí),發(fā)現(xiàn)資源計(jì)數(shù)為 0,將釋放其持有的資源。多個(gè)線程之間,遞增和減少資源的引用計(jì)數(shù)是安全的。(注意:這不意味著多個(gè)線程同時(shí)操作 std::shared_ptr 引用的對(duì)象是安全的)。

        std::shared_ptr 提供了一個(gè) use_count() 方法來(lái)獲取當(dāng)前持有資源的引用計(jì)數(shù)。除了上面描述的,std::shared_ptr 用法和 std::unique_ptr 基本相同。

        下面是一個(gè)初始化 std::shared_ptr 的示例:

        int?main()
        {
        ????//初始化方式1
        ????std::shared_ptr?sp1(new?int(123));

        ????//初始化方式2
        ????std::shared_ptr?sp2;
        ????sp2.reset(new?int(123));

        ????//初始化方式3
        ????std::shared_ptr?sp3;
        ????sp3?=?std::make_shared(123);

        ????return?0;
        }

        和 std::unique_ptr 一樣,你應(yīng)該優(yōu)先使用 std::make_shared 去初始化一個(gè) std::shared_ptr 對(duì)象。

        再來(lái)看另外一段代碼:

        #include?
        #include?

        class?A
        {
        public:
        ????A()
        ????{
        ????????std::cout?<"A?constructor"?<????}

        ????~A()
        ????{
        ????????std::cout?<"A?destructor"?<????}
        };

        int?main()
        {
        ????{
        ????????//初始化方式1
        ????????std::shared_ptr?sp1(new?A());

        ????????std::cout?<"use?count:?"?<
        ????????//初始化方式2
        ????????std::shared_ptr
        ?sp2(sp1);
        ????????std::cout?<"use?count:?"?<
        ????????sp2.reset();
        ????????std::cout?<"use?count:?"?<
        ????????{
        ????????????std::shared_ptr
        ?sp3?=?sp1;
        ????????????std::cout?<"use?count:?"?<????????}

        ????????std::cout?<"use?count:?"?<????}

        ????return?0;
        }

        所以整個(gè)程序的執(zhí)行結(jié)果如下:

        A?constructor
        use?count:?1
        use?count:?2
        use?count:?1
        use?count:?2
        use?count:?1
        A?destructor

        上述代碼 22 行 sp1 構(gòu)造時(shí),同時(shí)觸發(fā)對(duì)象 A 的構(gòu)造,因此 A 的構(gòu)造函數(shù)會(huì)執(zhí)行。

        此時(shí)只有一個(gè) sp1 對(duì)象引用 22 行 new 出來(lái)的 A 對(duì)象(為了敘述方便,下文統(tǒng)一稱之為資源對(duì)象 A),因此代碼 24 行打印出來(lái)的引用計(jì)數(shù)值為 1。

        代碼 27 行,利用 sp1 拷貝一份 sp2,導(dǎo)致代碼 28 行打印出來(lái)的引用計(jì)數(shù)為 2。

        代碼 30 行調(diào)用 sp2 的 reset() 方法,sp2 釋放對(duì)資源對(duì)象 A 的引用,因此代碼 31 行打印的引用計(jì)數(shù)值再次變?yōu)?1。

        代碼 34 行 利用 sp1 再次 創(chuàng)建 sp3,因此代碼 35 行打印的引用計(jì)數(shù)變?yōu)?2。

        程序執(zhí)行到 36 行以后,sp3 出了其作用域被析構(gòu),資源 A 的引用計(jì)數(shù)遞減 1,因此 代碼 38 行打印的引用計(jì)數(shù)為 1。

        程序執(zhí)行到 39 行以后,sp1 出了其作用域被析構(gòu),在其析構(gòu)時(shí)遞減資源 A 的引用計(jì)數(shù)至 0,并析構(gòu)資源 A 對(duì)象,因此類 A 的析構(gòu)函數(shù)被調(diào)用。

        1、std::enable_shared_from_this

        實(shí)際開發(fā)中,有時(shí)候需要在類中返回包裹當(dāng)前對(duì)象(this)的一個(gè) std::shared_ptr 對(duì)象給外部使用,C++ 新標(biāo)準(zhǔn)也為我們考慮到了這一點(diǎn),有如此需求的類只要繼承自 std::enable_shared_from_this 模板對(duì)象即可。用法如下:

        #include?
        #include?

        class?A?:?public?std::enable_shared_from_this

        {
        public:
        ????A()
        ????{
        ????????std::cout?<"A?constructor"?<????}

        ????~A()
        ????{
        ????????std::cout?<"A?destructor"?<????}

        ????std::shared_ptr
        ?getSelf()
        ????{
        ????????return?shared_from_this();
        ????}
        };

        int?main()
        {
        ????std::shared_ptr
        ?sp1(new?A());

        ????std::shared_ptr
        ?sp2?=?sp1->getSelf();

        ????std::cout?<"use?count:?"?<
        ????return?0;
        }

        上述代碼中,類 A 的繼承 std::enable_shared_from_this 并提供一個(gè) getSelf() 方法返回自身的 std::shared_ptr 對(duì)象,在 getSelf() 中調(diào)用 shared_from_this() 即可。

        std::enable_shared_from_this 用起來(lái)比較方便,但是也存在很多不易察覺的陷阱。

        2、陷阱一:不應(yīng)該共享?xiàng)?duì)象的 this 給智能指針對(duì)象

        假設(shè)我們將上面代碼 main 函數(shù) 25 行生成 A 對(duì)象的方式改成一個(gè)棧變量,即:

        //其他相同代碼省略...

        int?main()
        {
        ????A?a;

        ????std::shared_ptr
        ?sp2?=?a.getSelf();

        ????std::cout?<"use?count:?"?<
        ????return?0;
        }

        運(yùn)行修改后的代碼會(huì)發(fā)現(xiàn)程序在 std::shared_ptr sp2 = a.getSelf() 產(chǎn)生崩潰。這是因?yàn)?,智能指針管理的是堆?duì)象,棧對(duì)象會(huì)在函數(shù)調(diào)用結(jié)束后自行銷毀,因此不能通過(guò) shared_from_this() 將該對(duì)象交由智能指針對(duì)象管理。

        切記:智能指針最初設(shè)計(jì)的目的就是為了管理堆對(duì)象的(即那些不會(huì)自動(dòng)釋放的資源)。

        3、陷阱二:避免 std::enable_shared_from_this 的循環(huán)引用問(wèn)題

        #include?
        #include?

        class?A?:?public?std::enable_shared_from_this

        {
        public:
        ????A()
        ????{
        ????????m_i?=?9;
        ????????//注意:
        ????????//比較好的做法是在構(gòu)造函數(shù)里面調(diào)用shared_from_this()給m_SelfPtr賦值
        ????????//但是很遺憾不能這么做,如果寫在構(gòu)造函數(shù)里面程序會(huì)直接崩潰

        ????????std::cout?<"A?constructor"?<????}

        ????~A()
        ????{
        ????????m_i?=?0;

        ????????std::cout?<"A?destructor"?<????}

        ????void?func()
        ????{
        ????????m_SelfPtr?=?shared_from_this();
        ????}

        public:
        ????int?m_i;
        ????std::shared_ptr
        ?m_SelfPtr;
        };

        int?main()
        {
        ????{
        ????????std::shared_ptr
        ?spa(new?A());
        ????????spa->func();
        ????}

        ????return?0;
        }

        乍一看上面的代碼好像看不出什么問(wèn)題,讓我們來(lái)實(shí)際運(yùn)行一下看看輸出結(jié)果:

        A?constructor

        我們發(fā)現(xiàn)在程序的整個(gè)生命周期內(nèi),只有 A 類構(gòu)造函數(shù)的調(diào)用輸出,沒有 A 類析構(gòu)函數(shù)的調(diào)用輸出,這意味著 new 出來(lái)的 A 對(duì)象產(chǎn)生了內(nèi)存泄漏!

        我們來(lái)分析一下為什么 new 出來(lái)的 A 對(duì)象得不到釋放。當(dāng)程序執(zhí)行到 39 行后,spa 出了其作用域準(zhǔn)備析構(gòu),在析構(gòu)時(shí)其發(fā)現(xiàn)仍然有另外的一個(gè) std::shared_ptr 對(duì)象即 A::m_SelfPtr 引用了 A,因此 spa 只會(huì)將 A 的引用計(jì)數(shù)遞減為 1,然后就銷毀自身了?,F(xiàn)在留下一個(gè)矛盾的處境:必須銷毀 A 才能銷毀其成員變量 m_SelfPtr,而銷毀 m_SelfPtr 必須先銷毀 A。這就是所謂的 std::enable_shared_from_this 的循環(huán)引用問(wèn)題。我們?cè)趯?shí)際開發(fā)中應(yīng)該避免做出這樣的邏輯設(shè)計(jì),這種情形下即使使用了智能指針也會(huì)造成內(nèi)存泄漏。也就是說(shuō)一個(gè)資源的生命周期可以交給一個(gè)智能指針對(duì)象,但是該智能指針的生命周期不可以再交給整個(gè)資源來(lái)管理。

        std::weak_ptr:

        std::weak_ptr 是一個(gè)不控制資源生命周期的智能指針,是對(duì)對(duì)象的一種弱引用,只是提供了對(duì)其管理的資源的一個(gè)訪問(wèn)手段,引入它的目的為協(xié)助 std::shared_ptr 工作。

        std::weak_ptr 可以從一個(gè) std::shared_ptr 或另一個(gè) std::weak_ptr 對(duì)象構(gòu)造,std::shared_ptr 可以直接賦值給 std::weak_ptr ,也可以通過(guò) std::weak_ptr 的 lock() 函數(shù)來(lái)獲得 std::shared_ptr。它的構(gòu)造和析構(gòu)不會(huì)引起引用計(jì)數(shù)的增加或減少。std::weak_ptr 可用來(lái)解決 std::shared_ptr 相互引用時(shí)的死鎖問(wèn)題(即兩個(gè)std::shared_ptr 相互引用,那么這兩個(gè)指針的引用計(jì)數(shù)永遠(yuǎn)不可能下降為 0, 資源永遠(yuǎn)不會(huì)釋放)。

        示例代碼如下:

        #include?
        #include?

        int?main()
        {
        ????//創(chuàng)建一個(gè)std::shared_ptr對(duì)象
        ????std::shared_ptr?sp1(new?int(123));
        ????std::cout?<"use?count:?"?<
        ????//通過(guò)構(gòu)造函數(shù)得到一個(gè)std::weak_ptr對(duì)象
        ????std::weak_ptr?sp2(sp1);
        ????std::cout?<"use?count:?"?<
        ????//通過(guò)賦值運(yùn)算符得到一個(gè)std::weak_ptr對(duì)象
        ????std::weak_ptr?sp3?=?sp1;
        ????std::cout?<"use?count:?"?<
        ????//通過(guò)一個(gè)std::weak_ptr對(duì)象得到另外一個(gè)std::weak_ptr對(duì)象
        ????std::weak_ptr?sp4?=?sp2;
        ????std::cout?<"use?count:?"?<
        ????return?0;
        }

        運(yùn)行結(jié)果:

        use?count:?1
        use?count:?1
        use?count:?1
        use?count:?1

        無(wú)論通過(guò)何種方式創(chuàng)建 std::weak_ptr 都不會(huì)增加資源的引用計(jì)數(shù),因此每次輸出引用計(jì)數(shù)的值都是 1。

        既然,std::weak_ptr 不管理對(duì)象的生命周期,那么其引用的對(duì)象可能在某個(gè)時(shí)刻被銷毀了,如何得知呢?std::weak_ptr 提供了一個(gè) expired() 方法來(lái)做這一項(xiàng)檢測(cè),返回 true,說(shuō)明其引用的資源已經(jīng)不存在了;返回 false,說(shuō)明該資源仍然存在,這個(gè)時(shí)候可以使用 std::weak_ptr 的 lock() 方法得到一個(gè) std::shared_ptr 對(duì)象然后繼續(xù)操作資源,以下代碼演示了該用法:

        //?tmpConn_?是一個(gè)?std::weak_ptr?對(duì)象
        //?tmpConn_?引用的TcpConnection已經(jīng)銷毀,直接返回
        if?(tmpConn_.expired())
        ????return;

        std::shared_ptr?conn?=?tmpConn_.lock();
        if?(conn)
        {
        ????//對(duì)conn進(jìn)行操作,省略...
        }

        有讀者可能對(duì)上述代碼產(chǎn)生疑問(wèn),既然使用了 std::weak_ptr 的 expired() 方法判斷了對(duì)象是否存在,為什么不直接使用 std::weak_ptr 對(duì)象對(duì)引用資源進(jìn)行操作呢?實(shí)際上這是行不通的,std::weak_ptr 類沒有重寫 operator-> 和 operator* 方法,因此不能像 std::shared_ptr 或 std::unique_ptr 一樣直接操作對(duì)象,同時(shí) std::weak_ptr 類也沒有重寫 operator! 操作,因此也不能通過(guò) std::weak_ptr 對(duì)象直接判斷其引用的資源是否存在:

        #include?

        class?A
        {
        public:
        ????void?doSomething()
        ????{
        ????}
        };

        int?main()
        {
        ????std::shared_ptr
        ?sp1(new?A());

        ????std::weak_ptr
        ?sp2(sp1);

        ????//正確代碼
        ????if?(sp1)
        ????{
        ????????//正確代碼
        ????????sp1->doSomething();
        ????????(*sp1).doSomething();
        ????}

        ????//正確代碼
        ????if?(!sp1)
        ????{
        ????}

        ????//錯(cuò)誤代碼,無(wú)法編譯通過(guò)
        ????//if?(sp2)
        ????//{
        ????//????//錯(cuò)誤代碼,無(wú)法編譯通過(guò)
        ????//????sp2->doSomething();
        ????//????(*sp2).doSomething();
        ????//}

        ????//錯(cuò)誤代碼,無(wú)法編譯通過(guò)
        ????//if?(!sp2)
        ????//{

        ????//}

        ????return?0;
        }

        之所以 std::weak_ptr 不增加引用資源的引用計(jì)數(shù)不管理資源的生命周期,是因?yàn)?,即使它?shí)現(xiàn)了以上說(shuō)的幾個(gè)方法,調(diào)用它們也是不安全的,因?yàn)樵谡{(diào)用期間,引用的資源可能恰好被銷毀了,這會(huì)造成棘手的錯(cuò)誤和麻煩。

        因此,std::weak_ptr 的正確使用場(chǎng)景是那些資源如果可能就使用,如果不可使用則不用的場(chǎng)景,它不參與資源的生命周期管理。例如,網(wǎng)絡(luò)分層結(jié)構(gòu)中,Session 對(duì)象(會(huì)話對(duì)象)利用 Connection 對(duì)象(連接對(duì)象)提供的服務(wù)工作,但是 Session 對(duì)象不管理 Connection 對(duì)象的生命周期,Session 管理 Connection 的生命周期是不合理的,因?yàn)榫W(wǎng)絡(luò)底層出錯(cuò)會(huì)導(dǎo)致 Connection 對(duì)象被銷毀,此時(shí) Session 對(duì)象如果強(qiáng)行持有 Connection 對(duì)象與事實(shí)矛盾。

        std::weak_ptr 的應(yīng)用場(chǎng)景,經(jīng)典的例子是訂閱者模式或者觀察者模式中。這里以訂閱者為例來(lái)說(shuō)明,消息發(fā)布器只有在某個(gè)訂閱者存在的情況下才會(huì)向其發(fā)布消息,而不能管理訂閱者的生命周期。

        class?Subscriber
        {
        };

        class?SubscribeManager
        {
        public:
        ????void?publish()
        ????{
        ????????for?(const?auto?&iter?:?m_subscribers)
        ????????{
        ????????????if?(!iter.expired())
        ????????????{
        ????????????????//TODO:給訂閱者發(fā)送消息
        ????????????}
        ????????}
        ????}

        private:
        ????std::vector>?m_subscribers;
        };

        智能指針的大小:

        一個(gè) std::unique_ptr 對(duì)象大小與裸指針大小相同(即 sizeof(std::unique_ptr) == sizeof(void)),而 std::shared_ptr 的大小是 std::unique_ptr 的一倍。以下是我分別在 Visual Studio 2019 和 gcc/g++ 4.8 上(二者都編譯成 x64 程序)的測(cè)試結(jié)果:

        #include?
        #include?
        #include?

        int?main()
        {
        ????std::shared_ptr?sp0;
        ????std::shared_ptr?sp1;
        ????sp1.reset(new?std::string());
        ????std::unique_ptr?sp2;
        ????std::weak_ptr?sp3;

        ????std::cout?<"sp0?size:?"?<????std::cout?<"sp1?size:?"?<????std::cout?<"sp2?size:?"?<????std::cout?<"sp3?size:?"?<
        ????return?0;
        }

        Visual Studio 2019 (32bit) 運(yùn)行結(jié)果:

        sp0?size:8
        sp1?size:8
        sp2?size:4
        sp3?size:8

        gcc/g++ (64bit) 運(yùn)行結(jié)果:

        sp0?size:16
        sp1?size:16
        sp2?size:8
        sp3?size:16

        在 32 位機(jī)器上,std_unique_ptr 占 4 字節(jié),std::shared_ptr 和 std::weak_ptr 占 8 字節(jié)。

        在 64 位機(jī)器上,std_unique_ptr 占 8 字節(jié),std::shared_ptr 和 std::weak_ptr 占 16 字節(jié)。

        也就是說(shuō),std_unique_ptr 的大小總是和原始指針大小一樣,std::shared_ptr 和 std::weak_ptr 大小是原始指針的一倍。

        智能指針使用注意事項(xiàng):

        C++ 新標(biāo)準(zhǔn)提倡的理念之一是不應(yīng)該再手動(dòng)調(diào)用 delete 或者 free 函數(shù)去釋放內(nèi)存了,而應(yīng)該把它們交給新標(biāo)準(zhǔn)提供的各種智能指針對(duì)象。C++ 新標(biāo)準(zhǔn)中的各種智能指針是如此的實(shí)用與強(qiáng)大,在現(xiàn)代 C++ 項(xiàng)目開發(fā)中,我們應(yīng)該盡量去使用它們。智能指針雖然好用,但稍不注意,也可能存在許多難以發(fā)現(xiàn)的 bug,這里我根據(jù)經(jīng)驗(yàn)總結(jié)了幾條:

        #include?

        class?Subscriber
        {
        };

        int?main()
        {
        ????Subscriber?*pSubscriber?=?new?Subscriber();

        ????std::unique_ptr?spSubscriber(pSubscriber);

        ????delete?pSubscriber;

        ????return?0;
        }

        這段代碼利用創(chuàng)建了一個(gè)堆對(duì)象 Subscriber,然后利用智能指針 spSubscriber 去管理之,可以卻私下利用原始指針銷毀了該對(duì)象,這讓智能指針對(duì)象 spSubscriber 情何以堪???

        記住,一旦智能指針對(duì)象接管了你的資源,所有對(duì)資源的操作都應(yīng)該通過(guò)智能指針對(duì)象進(jìn)行,不建議再通過(guò)原始指針進(jìn)行操作了。

        當(dāng)然,除了 std::weak_ptr 之外,std::unique_ptr 和 std::shared_ptr 都提供了獲取原始指針的方法——get() 函數(shù)。

        int?main()
        {
        ????Subscriber?*pSubscriber?=?new?Subscriber();

        ????std::unique_ptr?spSubscriber(pSubscriber);

        ????//pTheSameSubscriber和pSubscriber指向同一個(gè)對(duì)象
        ????Subscriber?*pTheSameSubscriber?=?spSubscriber.get();

        ????return?0;
        }

        通常情況下,如果你的資源不需要在其他地方共享,那么應(yīng)該優(yōu)先使用 std::unique_ptr,反之使用 std::shared_ptr,當(dāng)然這是在該智能指針需要管理資源的生命周期的情況下;如果不需要管理對(duì)象的生命周期,請(qǐng)使用 std::weak_ptr。

        前面的例子,一定讓你覺得非常容易知道一個(gè)智能指針的持有的資源是否還有效,但是還是建議在不同場(chǎng)景謹(jǐn)慎一點(diǎn),有些場(chǎng)景是很容易造成誤判。例如下面的代碼:

        #include?
        #include?

        class?T
        {
        public:
        ????void?doSomething()
        ????{
        ????????std::cout?<"T?do?something..."?<????}

        private:
        ????int?m_i;
        };

        int?main()
        {
        ????std::shared_ptr?sp1(new?T());
        ????const?auto?&sp2?=?sp1;

        ????sp1.reset();

        ????//由于sp2已經(jīng)不再持有對(duì)象的引用,程序會(huì)在這里出現(xiàn)意外的行為
        ????sp2->doSomething();

        ????return?0;
        }

        上述代碼中,sp2 是 sp1 的引用,sp1 被置空后,sp2 也一同為空。這時(shí)候調(diào)用 sp2->doSomething(),sp2->(即 operator->)在內(nèi)部會(huì)調(diào)用 get() 方法獲取原始指針對(duì)象,這時(shí)會(huì)得到一個(gè)空指針(地址為 0),繼續(xù)調(diào)用 doSomething() 導(dǎo)致程序崩潰。

        你一定仍然覺得這個(gè)例子也能很明顯地看出問(wèn)題,ok,讓我們把這個(gè)例子放到實(shí)際開發(fā)中再來(lái)看一下:

        //連接斷開
        void?MonitorServer::OnClose(const?std::shared_ptr?&conn)
        {
        ????std::lock_guard?guard(m_sessionMutex);
        ????for?(auto?iter?=?m_sessions.begin();?iter?!=?m_sessions.end();?++iter)
        ????{
        ????????//通過(guò)比對(duì)connection對(duì)象找到對(duì)應(yīng)的session
        ????????if?((*iter)->GetConnectionPtr()?==?conn)
        ????????{
        ????????????m_sessions.erase(iter);
        ????????????//注意這里:程序在此處崩潰
        ????????????LOGI("monitor?client?disconnected:?%s",?conn->peerAddress().toIpPort().c_str());
        ????????????break;
        ????????}
        ????}
        }

        該段程序會(huì)在代碼 12 行處崩潰,崩潰原因是調(diào)用了 conn->peerAddress() 方法。為什么這個(gè)方法的調(diào)用可能會(huì)引起崩潰?現(xiàn)在可以一目了然地看出了嗎?

        崩潰原因是傳入的 conn 對(duì)象和上一個(gè)例子中的 sp2 一樣都是另外一個(gè) std::shared_ptr 的引用,當(dāng)連接斷開時(shí),對(duì)應(yīng)的 TcpConnection 對(duì)象可能早已被銷毀,而 conn 引用就會(huì)變成空指針(嚴(yán)格來(lái)說(shuō)是不再擁有一個(gè) TcpConnection 對(duì)象),此時(shí)調(diào)用 TcpConnection 的 peerAddress() 方法就會(huì)產(chǎn)生和上一個(gè)示例一樣的錯(cuò)誤。

        我們知道,為了減小編譯依賴加快編譯速度和生成二進(jìn)制文件的大小,C/C++ 項(xiàng)目中一般在 *.h 文件對(duì)于指針類型盡量使用前置聲明,而不是直接包含對(duì)應(yīng)類的頭文件。例如:

        //Test.h
        //在這里使用A的前置聲明,而不是直接包含A.h文件
        class?A;

        class?Test
        {
        public:
        ????Test();
        ????~Test();

        private:
        ????A?*m_pA;
        };

        同樣的道理,在頭文件中當(dāng)使用智能指針對(duì)象作為類成員變量時(shí),也應(yīng)該優(yōu)先使用前置聲明去引用智能指針對(duì)象的包裹類,而不是直接包含包含類的頭文件。

        //Test.h
        #include?

        //智能指針包裹類A,這里優(yōu)先使用A的前置聲明,而不是直接包含A.h
        class?A;

        class?Test
        {
        public:
        ????Test();
        ????~Test();

        private:
        ????std::unique_ptr
        ?m_spA;
        };

        Modern C/C++ 已經(jīng)變?yōu)?C/C++ 開發(fā)的趨勢(shì),希望能善用和熟練這些智能指針對(duì)象。

        智能指針的簡(jiǎn)單實(shí)現(xiàn):

        最后,給出智能指針的簡(jiǎn)單實(shí)現(xiàn),因?yàn)?weak_ptr 作為弱引用指針,其實(shí)現(xiàn)依賴于 Counter 計(jì)數(shù)器類和 shared_ptr 的賦值,所以先進(jìn)行 Counter 計(jì)數(shù)器類和 share_ptr 的簡(jiǎn)單實(shí)現(xiàn)。

        /*
        ?*?計(jì)數(shù)器
        ?*?Counter對(duì)象就是用來(lái)申請(qǐng)一塊內(nèi)存存儲(chǔ)引用計(jì)數(shù)
        ?*?m_refCount是SharedPtr的引用計(jì)數(shù)
        ?*?m_weakCount是WeakPtr的引用計(jì)數(shù)
        ?*?當(dāng)m_weakCount為0時(shí)刪除Counter對(duì)象
        ?*/
        template?
        class?Counter
        {
        ????friend?class?SharedPtr;
        ????friend?class?WeakPtr;

        public:
        ????Counter()?:?m_refCount(0),?m_weakCount(0)?{}
        ????virtual?~Counter()?{}

        private:
        ????Counter(const?Counter?&)?=?delete;
        ????Counter?&operator=(const?Counter?&)?=?delete;

        private:
        ????atomic_uint?m_refCount;??//?#shared,原子操作
        ????atomic_uint?m_weakCount;?//?#weak,原子操作
        };

        /*
        ?*?SharedPtr的簡(jiǎn)單實(shí)現(xiàn)
        ?*/
        template?
        class?SharedPtr
        {

        ????friend?class?WeakPtr;

        public:
        ????/*
        ?????*?構(gòu)造函數(shù),用原生指針構(gòu)造
        ?????*/
        ????SharedPtr(T?*ptr)?:?m_ptr(ptr),?m_cnt(new?Counter)
        ????{
        ????????if?(ptr)
        ????????{
        ????????????m_cnt->m_refCount?=?1;
        ????????}
        ????????cout?<"Ptr?Construct?S."?<????}

        ????~SharedPtr()
        ????{
        ????????release();
        ????}
        ????/*
        ?????*?拷貝構(gòu)造函數(shù),用另一個(gè)SharedPtr對(duì)象構(gòu)造
        ?????*/
        ????SharedPtr(const?SharedPtr?&s)
        ????{
        ????????m_ptr?=?s.m_ptr;
        ????????s.m_cnt->m_refCount++;
        ????????m_cnt?=?s.m_cnt;
        ????????cout?<"S?Copy?Construct?S."?<????}

        ????/*
        ?????*?拷貝構(gòu)造函數(shù),用另一個(gè)WeakPtr對(duì)象構(gòu)造
        ?????*?為了WeakPtr對(duì)象調(diào)用自己的lock()方法將自己傳進(jìn)來(lái)構(gòu)造一個(gè)SharedPtr返回
        ?????*/
        ????SharedPtr(const?WeakPtr?&w)
        ????{
        ????????m_ptr?=?w.m_ptr;
        ????????w.m_cnt->m_refCount++;
        ????????m_cnt?=?w.m_cnt;
        ????????cout?<"W?Copy?Construct?S."?<????}

        ????/*
        ?????*?賦值構(gòu)造函數(shù),用另一個(gè)SharedPtr對(duì)象構(gòu)造
        ?????*/
        ????SharedPtr?&operator=(const?SharedPtr?&s)
        ????{
        ????????if?(this?!=?s)
        ????????{
        ????????????this->release();
        ????????????m_ptr?=?s.m_ptr;
        ????????????s.m_cnt->m_refCount++;
        ????????????m_cnt?=?s.m_cnt;
        ????????????cout?<"S?Assign?Construct?S."?<????????}
        ????????return?*this;
        ????}

        ????T?&operator*()
        ????{
        ????????return?*m_ptr;
        ????}

        ????T?*operator->()
        ????{
        ????????return?m_ptr;
        ????}

        protected:
        ????void?release()
        ????{
        ????????m_cnt->m_refCount--;
        ????????if?(m_cnt->m_refCount?????????{
        ????????????delete?m_ptr;
        ????????????m_ptr?=?nullptr;
        ????????????cout?<"SharedPtr?Delete?Ptr."?<????????????if?(m_cnt->m_weakCount?????????????{
        ????????????????delete?m_cnt;
        ????????????????m_cnt?=?nullptr;
        ????????????????cout?<"SharedPtr?Delete?Cnt."?<????????????}
        ????????????cout?<"SharedPtr?Release."?<????????}
        ????}

        private:
        ????T?*m_ptr;
        ????Counter?*m_cnt;
        };

        template?
        class?WeakPtr
        {
        public:
        ????/*
        ?????*?構(gòu)造函數(shù),用SharedPtr對(duì)象構(gòu)造
        ?????*/
        ????WeakPtr(SharedPtr?&s)?:?m_ptr(s.m_ptr),?m_cnt(s.m_cnt)
        ????{
        ????????m_cnt->m_weakCount++;
        ????????cout?<"S?Construct?W."?<????}
        ????/*
        ?????*?構(gòu)造函數(shù),用WeakPtr對(duì)象構(gòu)造
        ?????*/
        ????WeakPtr(WeakPtr?&w)?:?m_ptr(w.m_ptr),?m_cnt(w.m_cnt)
        ????{
        ????????m_cnt->m_weakCount++;
        ????????cout?<"W?Construct?W."?<????}

        ????~WeakPtr()
        ????{
        ????????release();
        ????}
        ????/*
        ?????*?賦值構(gòu)造函數(shù),用另一個(gè)SharedPtr對(duì)象構(gòu)造
        ?????*/
        ????WeakPtr?&operator=(SharedPtr?&s)
        ????{
        ????????release();
        ????????m_cnt?=?s.m_cnt;
        ????????m_cnt->m_weakCount++;
        ????????m_ptr?=?s.m_ptr;
        ????????cout?<"S?Assign?Construct?W."?<????????return?*this;
        ????}
        ????/*
        ?????*?賦值構(gòu)造函數(shù),用另一個(gè)WeakPtr對(duì)象構(gòu)造
        ?????*/
        ????WeakPtr?&operator=(WeakPtr?&w)
        ????{
        ????????if?(this?!=?&w)
        ????????{
        ????????????release();
        ????????????m_cnt?=?w.m_cnt;
        ????????????m_cnt->m_weakCount++;
        ????????????m_ptr?=?w->m_ptr;
        ????????????cout?<"W?Assign?Construct?W."?<????????}
        ????????return?*this;
        ????}
        ????/*
        ?????*?WeakPtr通過(guò)lock函數(shù)獲得SharedPtr
        ?????*/
        ????SharedPtr?&lock()
        ????{
        ????????return?SharedPtr(*this);
        ????}
        ????/*
        ?????*?檢查SharedPtr是否已過(guò)期
        ?????*/
        ????bool?expired()
        ????{
        ????????if?(m_cnt)
        ????????{
        ????????????if?(m_cnt->m_refCount?>?0)
        ????????????????return?false;
        ????????}
        ????????return?true;
        ????}

        private:
        ????WeakPtr()?=?delete;???????WeakPtr禁止默認(rèn)構(gòu)造,只能從SharedPtr或者WeakPtr構(gòu)造
        ????T?&operator*()?=?delete;??//WeakPtr禁止*
        ????T?*operator->()?=?delete;?//WeakPtr禁止->

        private:
        ????void?release()
        ????{
        ????????if?(m_cnt)
        ????????{
        ????????????m_cnt->m_weakCount--;
        ????????????if?(m_cnt->m_weakCount?m_refCount?????????????{
        ????????????????delete?m_cnt;
        ????????????????m_cnt?=?nullptr;
        ????????????????cout?<"Delete?Cnt."?<????????????}
        ????????????cout?<"WeakPtr?Release."?<????????}
        ????}

        private:
        ????T?*m_ptr;
        ????Counter?*m_cnt;
        };

        上面的實(shí)現(xiàn)可能不是非常嚴(yán)謹(jǐn),僅實(shí)現(xiàn)了常用的的函數(shù)接口而已,但其主要的目的是為了更深刻的了解智能指針的原理,這樣才能更有把握的使用智能指針,只有了解它的內(nèi)部實(shí)現(xiàn),對(duì)于使用中的一些坑才能有效避免。

        文章相關(guān)參考:https://blog.csdn.net/code_peak/article/details/119722167

        文章整理:txp玩Linux


        推薦:

        面試常問(wèn)的 C/C++ 問(wèn)題,你能答上來(lái)幾個(gè)?

        C++ 面試必問(wèn):深入理解虛函數(shù)表

        很多人搞不清 C++ 中的 delete 和 delete[ ] 的區(qū)別

        看懂別人的代碼,總得懂點(diǎn) C++ lambda 表達(dá)式吧

        Java、C++ 內(nèi)存模型都不知道,還敢說(shuō)自己是高級(jí)工程師?

        C++ std::thread 必須要熟悉的幾個(gè)知識(shí)點(diǎn)

        現(xiàn)代 C++ 并發(fā)編程基礎(chǔ)

        現(xiàn)代 C++ 智能指針使用入門

        c++ thread join 和 detach 到底有什么區(qū)別?

        C++ 面試八股文:list、vector、deque 比較

        瀏覽 24
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        評(píng)論
        圖片
        表情
        推薦
        點(diǎn)贊
        評(píng)論
        收藏
        分享

        手機(jī)掃一掃分享

        分享
        舉報(bào)
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            激情五月天婷婷丁香 | 下载国产一级黄色片 | 国产综合久久久久鬼色 | 中国老太卖婬视频播放 | 日韩A片在线观看 | 国产女优在线视频 | 办公室女职员交换性bd | 国产偷拍自拍第一第一页 | 操逼国产无码 | 国产在线拍揄自揄拍无码网站新闻 |