<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                Item 20:Use `std::weak_ptr` for `std::shared_ptr` like pointers that can dangle. ========================= 說起來有些矛盾,可以很方便的創建一個表現起來想`std::shared_ptr`的智能指針,但是它卻不會參于被指向資源的共享式管理。換句話說,一個類似于`std::shared_ptr`的指針不影響它所指對象的引用計數。這種類型的智能指針必須面臨一個`std::shared_ptr`未曾面對過的問題:它所指向的對象可能已經被析構。一個真正的智能指針通過持續跟蹤判斷它是否已經懸掛(dangle)來處理這種問題,懸掛意味著它指向的對象已經不復存在。這就是`std::weak_ptr`的功能所在 你可能懷疑`std::weak_ptr`怎么會有用,當你檢查了下`std::weak_ptr`的API之后,你會覺得更奇怪。它的API看起來一點都不智能。`std::weak_ptr`不能被解引用,也不能檢測判空。這是因為`std::weak_ptr`不能被單獨使用,它是`std::shared_ptr`作為參數的產物。 這種關系與生俱來,`std::weak_ptr`通常由一個`std::shared_ptr`來創建,它們指向相同的地方,`std::shared_ptr`來初始化它們,但是`std::weak_ptr`不會影響到它所指向對象的引用計數: ```cpp auto spw = std::make_shared<Widget>();//spw 被構造之后 //被指向的Widget對象的引用計數為1 //(欲了解std::make_shared詳情,請看Item21) ... std::weak_ptr<Widget> wpw(spw);//wpw和spw指向了同一個Widget,但是RC(這里指引用計數,下同)仍舊是1 ... spw = nullptr;//RC變成了0,Widget也被析構,wpw現在處于懸掛狀態 ``` 懸掛的std::weak_ptr可以稱作是過期了(expired),可以直接檢查是否過期: ```cpp if(wpw.expired())... //如果wpw懸掛... ``` 但是我們最經常的想法是:查看`std::weak_ptr`是否已經過期,如果沒有過期的話,訪問它所指向的對象。想的容易做起來難啊。因為`std::weak_ptr`缺少解引用操作,也就沒辦法寫完成這樣操作的代碼。即使又沒法做到,將檢查和解引用分開的寫法也會引入一個競態存在:在調用expired以及解引用操作之間,另外一個線程可能對被指向的對象重新賦值或者摧毀了最后一個指向對象的`std::shared_ptr`,這樣就導致了被指向的對象的析構。這種情況下,你的解引用操作會產生未定義行為。 我們需要的是將檢查`std::weak_ptr`是否過期,以及如果未過期的話獲得訪問所指對象的權限這兩種操作合成一個原子操作。這是通過由`std::weak_ptr`創建出一個`std::shared_ptr`來完成的。根據當`std::weak_ptr`已經過期,仍以它為參數創建`std::shared_ptr`會發生的情況的不同,這種創建有兩種方式。一種方式是通過`std::weak_ptr::lock`,它會返回一個`std::shared_ptr`,當`std::weak_ptr`已經過期時,`std::shared_ptr`會是null: ```cpp std::shared_ptr<Widget> spw1 = wpw.lock();//如果wpw已經過期 //spw1的值是null auto spw2 = wpw.lock();//結果同上,這里使用了auto ``` 另外一種方式是以`std::weak_ptr`為參數,使用`std::shared_ptr`構造函數。這種情況下,如果`std::weak_ptr`過期的話,會有異常拋出: ```cpp std::shared_ptr<Widget> spw3(wpw);//如果wpw過期的話 //拋出std::bad_weak_ptr異常 ``` 你可能會產生疑問,`std::weak_ptr`到底有啥用。下面我們舉個例子,假如說現在有一個工廠函數,根據一個唯一的ID,返回一個指向只讀對象的智能指針。根據Item 18關于工廠函數返回類型的建議,它應該返回一個`std::unique_ptr`: ```cpp std::unique_ptr<const Widget> loadWidget(WidgetID id); ``` 如果loadWidget調用的代價不菲(比如,它涉及到了文件或數據庫的I/O操作),而且ID的使用也比較頻繁,一個合理的優化就是再寫一個函數,不僅完成loadWidget所做的事情,而且要緩存loadWidget的返回結果。把每一個請求過的Widget對象都緩存起來肯定會導致緩存自身的性能出現問題,所以,一個合理的做法是當被緩存的Widget不再使用時將它銷毀。 對于這樣的一個帶有緩存的工廠函數,返回`std::unique_ptr`類型不是一個很好的選擇。可以確定的兩點是:調用者接收指向緩存對象的智能指針,調用者來決定這些緩存對象的生命周期;但是,緩存也需要一個指向所緩存對象的指針。因為當工廠函數的調用者使用完了一個工廠返回的對象,這個對象會被銷毀,對應的緩存項會懸掛,所以緩存的指針需要有檢測它現在是否處于懸掛狀態的能力。因此緩存使用的指針應該是std::weak_ptr類型,它有檢測懸掛的能力。這就意味著工廠函數的返回類型應該是`std::shared_ptr`,因為只有當一個對象的生命周期被`std::shared_ptr`所管理時,`std::weak_ptr`才能檢測它自身是否處于懸掛狀態。 下面是一個較快卻欠缺完美的緩存版本的loadWidget的實現: ```cpp std::shared_ptr<const Widget> fastLoadWidget(WidgetId id) { static std::unordered_map<WidgetID, std::weak_ptr<const Widget>> cache; auto objPtr = cache[id].lock();//objPtr是std::shared_ptr類型 //指向了被緩存的對象(如果對象不在緩存中則是null) if(!objPtr){ objPtr = loadWidget(id); cache[id] = objPtr; }//如果不在緩存中,載入并且緩存它 return objPtr; } ``` C++11利用了hash表容器(`std::unordered_map`),盡管它沒有提供所需的WidgetID哈希算法以及相等比較函數。 我為啥要說fastLoadWidget實現欠缺完美,因為它忽略了一個事實,緩存可能把一些已經過期的`std::weak_ptr`(對應的Widget不會被使用了,已經被銷毀了)。所以它的實現還可以再改善下,但是我們還是不要深究了,因為深究對我們繼續深入了解`std::weak_ptr`沒有用處。我們下面探究第二個使用`std::weak_ptr`的場景:在觀察者模式中,主要的組成部分是:狀態可能會發生變化的subjects,以及當狀態變化時需要得到通知的observers.在大多數實現中,每一個subject包含了指向它的observers的數據成員.這就使得subject很容易發送出狀態變化的通知。subject對于控制他們的observer的生命周期(observer何時被析構)毫無興趣.但是,它們必須知道,如果一個observer析構了,subject就不能嘗試去訪問它了。一個合理的設計是:每一個subject擁有一個`std::weak_ptr`,指向了它的observer,這樣在可以在訪問之間,先檢查一下指針是否處于懸掛狀態。 下面講到最后一個`std::weak_ptr`的例子,有這樣一個數據結構,包含A,B和C。A和C共享B的所有權,它們各自包含了一個`std::shared_ptr`指向B ![20-1.png] 如果現在有需要使B擁有反向指針指向A,那么指針應該是什么類型? ![20-2.png] 下面有三種選擇: * __一個原生指針__。如果這么做,A如果被析構了,但是C會繼續指向B,B包含的指向A的指針現在處于懸掛狀態。而B對此毫不知情,所以B有可能不小心反引用了那個懸掛指針,這樣會產生未定義的行為。 * __一個`std::shared_ptr`__。在這種設計下,A和B包含了`std::shared_ptr`互相指向對方。結果就引發了一個`std::shared_ptr`的環(A指向B,B指向A),這個環會使得A和B都不能得到析構。即使程序其他的數據結構都不能訪問到A和B(例如,C如果不再指向B),A和B的引用計數仍然是1.如果這種情況發生了,A和B都會是內存泄露的情況,實際上,程序永遠無法再訪問到它們,它們也永遠無法得到回收。 * __一個`std::weak_ptr`__。這樣避免了以上所有的問題。如果A被回收,B指向它的指針將會懸掛,B也有能力檢測到這一狀態。此外,就算A和B互相指向對方,B的指針也不會影響到A的引用計數。當沒有`std::shared_ptr`指向A時,也不會阻止A的析構。 使用`std::weak_ptr`毫無疑問是最好的選擇。然而,值得注意的是,使用`std::weak_ptr`來破壞預期的`std::shared_ptr`形成的環不是那么普遍。在定義的比較嚴格的數據結構,比如說樹,子節點一般被父節點所擁有。當父節點被析構時,子節點也應該會被析構。從父節點指向子節點的鏈接因此最好使用std::unique_ptr.因為子節點不應該比父節點存在的時間過長,從子節點指向父節點的鏈接可以安全的使用原生指針來實現。因此也不會出現子節點解引用一個指向父節點的懸掛指針。 當然,并不是所有的以指針為基礎的數據結構都是嚴格的層級關系。如果不是的話,就像剛才所說的緩存以及觀察者列表的情形,使用`std::weak_ptr`是最棒的選擇了。 從效率的觀點來看,`std::weak_ptr`和`std::shared_ptr`的情況基本相同,。`std::weak_ptr`對象的大小和`std::shared_ptr`對象相同,它們都利用了同樣的控制塊(請看Item 19),并且諸如構造,析構以及賦值都涉及到引用計數的原子操作。這可能讓你吃了一驚,因為我在本章開始的時候說`std::weak_ptr`不參與引用計數的操作。可能沒有表達完整我的意思。我要寫的意思是`std::weak_ptr`不參與對象的共享所有權,因此不影響被指向對象的引用計數。但是,實際上在控制塊中存在第二個引用計數,`std::weak_ptr`來操作這個引用計數。欲知詳情,請看Item 21. |要記住的東西| |:--------- | |`std::weak_ptr`用來模仿類似std::shared_ptr的可懸掛指針| |潛在的使用`std::weak_ptr`的場景包括緩存,觀察者列表,以及阻止`std::shared_ptr`形成的環|
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看