<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>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                # Item 13: 使用對象管理資源 作者:Scott Meyers 譯者:fatalerror99 (iTePub's Nirvana) 發布:http://blog.csdn.net/fatalerror99/ 假設我們和一個投資(例如,股票,債券等)模型庫一起工作,各種各樣的投資形式從一個根類 Investment 派生出來: ``` class Investment { ... }; // root class of hierarchy of // investment types ``` 進一步假設這個庫使用了通過一個 factory 函數(參見 Item 7)為我們提供特定 Investment 對象的方法: ``` Investment* createInvestment(); // return ptr to dynamically allocated // object in the Investment hierarchy; // the caller must delete it // (parameters omitted for simplicity) ``` 通過注釋指出,當 createInvestment 函數返回的對象不再使用時,由 createInvestment 的調用者負責刪除它。那么,請考慮,寫一個函數 f 來履行以下職責: ``` void f() { Investment *pInv = createInvestment(); // call factory function ... // use pInv delete pInv; // release object } ``` 這個看上去沒問題,但是有幾種情形會造成 f 在刪除它從 createInvestment 得到的 investment 對象時失敗。有可能在這個函數的 "..." 部分的某處有一個提前出現的 return 語句。如果這樣一個 return 執行了,控制流程就再也無法到達 delete 語句。還可能發生的一個類似情況是如果 createInvestment 的使用和刪除在一個循環里,而這個循環以一個 continue 或 goto 語句提前退出。還有,"..." 中的一些語句可能拋出一個異常。如果這樣,控制流程不會再到達那個 delete。無論那個 delete 被如何跳過,我們泄漏的不僅僅是容納 investment 對象的內存,還包括那個對象持有的任何資源。 當然,小心謹慎地編程能防止這各種錯誤,但考慮到這些代碼可能會隨著時間的流逝而發生變化。為了對軟件進行維護,一些人可能會在沒有完全把握對這個函數的資源管理策略的其它部分的影響的情況下增加一個 return 或 continue 語句。尤有甚者,f 的 "..." 部分可能調用了一個從不慣于拋出異常的函數,但是在它被“改良”后突然這樣做了。依賴于 f 總能到達它的 delete 語句根本靠不住。 為了確保 createInvestment 返回的資源總能被釋放,我們需要將那些資源放入一個類中,這個類的析構函數在控制流程離開 f 的時候會自動釋放資源。實際上,這只是本 Item 介紹的觀念的一半:將資源放到一個對象的內部,我們可以依賴 C++ 的自動地調用析構函數來確保資源被釋放。(過一會兒我們還要介紹本 Item 觀念的另一半。) 許多資源都是動態分配到堆上的,并在一個單獨的塊或函數內使用,而且應該在控制流程離開那個塊或函數的時候釋放。標準庫的 auto_ptr 正是為這種情形量體裁衣的。auto_ptr 是一個類似指針的對象(一個智能指針),它的析構函數自動在它指向的東西上調用 delete。下面就是如何使用 auto_ptr 來預防 f 的潛在的資源泄漏: ``` void f() { std::auto_ptr<Investment> pInv(createInvestment()); // call factory // function ... // use pInv as // before } // automatically // delete pInv via // auto_ptr's dtor ``` 這個簡單的例子示范了使用對象管理資源的兩個重要的方面: * 獲得資源后應該立即移交給資源管理對象。如上,createInvestment 返回的資源被用來初始化即將用來管理它的 auto_ptr。實際上,因為獲取一個資源并在同一個語句中初始化資源管理對象是如此常見,所以使用對象管理資源的觀念也常常被稱為 Resource Acquisition Is Initialization (RAII)。有時被獲取的資源是被賦值給資源管理對象的,而不是初始化它們,但這兩種方法都是在獲取資源的同時就立即將它移交給資源管理對象。 * 資源管理對象使用它們的析構函數確保資源被釋放。因為當一個對象被銷毀時(例如,當一個對象離開其活動范圍)會自動調用析構函數,無論控制流程是怎樣離開一個塊的,資源都會被正確釋放。如果釋放資源的動作會引起異常拋出,事情就會變得棘手,不過,關于那些問題請訪問 Item 8,所以我們不必擔心它。 因為當一個 auto_ptr 被銷毀的時候,會自動刪除它所指向的東西,所以不要讓超過一個的 auto_ptr 指向同一個對象非常重要。如果發生了這種事情,那個對象就會被刪除超過一次,而且會讓你的程序通過捷徑進入未定義行為。為了防止這個問題,auto_ptrs 具有不同尋常的特性:拷貝它們(通過拷貝構造函數或者拷貝賦值運算符)就是將它們置為空,拷貝的指針被設想為資源的唯一所有權。 ``` sstd::auto_ptr<Investment> // pInv1 points to the pInv1(createInvestment()); // object returned from // createInvestment std::auto_ptr<Investment> pInv2(pInv1); // pInv2 now points to the // object; pInv1 is now null pInv1 = pInv2; // now pInv1 points to the // object, and pInv2 is null ``` 這個奇怪的拷貝行為,增加了潛在的需求,就是通過 auto_ptrs 管理的資源必須絕對沒有超過一個 auto_ptr 指向它們,這也就意味著 auto_ptrs 不是管理所有動態分配資源的最好方法。例如,STL 容器要求其內含物能表現出“正常的”拷貝行為,所以 auto_ptrs 的容器是不被允許的。 相對于 auto_ptrs,另一個可選方案是一個引用計數智能指針(reference-counting smart pointer, RCSP)。一個 RCSP 是一個智能指針,它能持續跟蹤有多少對象指向一個特定的資源,并能夠在不再有任何東西指向那個資源的時候刪除它。就這一點而論,RCSP 提供的行為類似于垃圾收集(garbage collection)。與垃圾收集不同的是,無論如何,RCSP 不能打破循環引用(例如,兩個沒有其它使用者的對象互相指向對方)。 TR1 的 tr1::shared_ptr(參見 Item 54)是一個 RCSP,所以你可以這樣寫 f: ``` void f() { ... std::tr1::shared_ptr<Investment> pInv(createInvestment()); // call factory function ... // use pInv as before } // automatically delete // pInv via shared_ptr's dtor ``` 這里的代碼看上去和使用 auto_ptr 的幾乎相同,但是拷貝 shared_ptrs 的行為卻自然得多: ``` void f() { ... std::tr1::shared_ptr<Investment> // pInv1 points to the pInv1(createInvestment()); // object returned from // createInvestment std::tr1::shared_ptr<Investment> // both pInv1 and pInv2 now pInv2(pInv1); // point to the object pInv1 = pInv2; // ditto — nothing has // changed ... } // pInv1 and pInv2 are // destroyed, and the // object they point to is // automatically deleted ``` 因為拷貝 tr1::shared_ptrs 的工作“符合預期”,它們能被用于 STL 容器以及其它和 auto_ptr 的非正統的拷貝行為不相容的環境中。 不要搞錯,本 Item 不是關于 auto_ptr,tr1::shared_ptr 或任何其它種類的智能指針。而是關于使用對象管理資源的重要性的。auto_ptr 和 tr1::shared_ptr 僅僅是做這些事的對象的例子。(關于 tr1::shared_ptr 的更多信息,請參考 Item 14,18 和 54。) auto_ptr 和 tr1::shared_ptr 都在它們的析構函數中使用 delete,而不是 delete []。(Item 16 描述兩者的差異。)這就意味著將 auto_ptr 或 tr1::shared_ptr 用于動態分配的數組是個餿主意,可是,可悲的是,那居然可以編譯: ``` std::auto_ptr<std::string> // bad idea! the wrong aps(new std::string[10]); // delete form will be used std::tr1::shared_ptr<int> spi(new int[1024]); // same problem ``` 你可能會吃驚地發現 C++ 中沒有可用于動態分配數組的類似 auto_ptr 或 tr1::shared_ptr 這樣的東西,甚至在 TR1 中也沒有。那是因為 vector 和 string 幾乎總是能代替動態分配數組。如果你依然覺得有可用于數組的類似 auto_ptr 和類似 tr1::shared_ptr 的類更好一些的話,可以去看看 Boost(參見 Item 55)。在那里,你將高興地找到 boost::scoped_array 和 boost::shared_array 兩個類提供你在尋找的行為。 本 Item 的關于使用對象管理資源的指導間接表明:如果你手動釋放資源(例如,使用 delete,而不使用資源管理類),你就是在自找麻煩。像 auto_ptr 和 tr1::shared_ptr 這樣的預制的資源管理類通常會使本 Item 的建議變得容易,但有時,你使用了一個資源,而這些預加工的類不能如你所愿地做事。如果碰上這種情況,你就需要精心打造你自己的資源管理類。那也并非困難得可怕,但它包含一些需要你細心考慮的微妙之處。那些需要考慮的事項是 Item 14 和 15 的主題。 作為最后的意見,我必須指出 createInvestment 的裸指針(raw pointer)的返回形式就是資源泄漏的請帖,因為調用者忘記在他們取回來的指針上調用 delete 實在是太容易了。(即使他們使用一個 auto_ptr 或 tr1::shared_ptr 來完成 delete,他們仍然必須記住將 createInvestment 的返回值存儲到智能指針對象中。)對付這個問題需要改變 createInvestment 的接口,這是我在 Item 18 中安排的主題。 Things to Remember * 為了防止資源泄漏,使用 RAII 對象,在 RAII 對象的構造函數中獲得資源并在析構函數中釋放它們。 * 兩個通用的 RAII 是 tr1::shared_ptr 和 auto_ptr。tr1::shared_ptr 通常是更好的選擇,因為它的拷貝時的行為是符合直覺的。拷貝一個 auto_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>

                              哎呀哎呀视频在线观看