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

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                # [17] 異常和錯誤處理 ## FAQs in section [17]: * [17.1] `try` / `catch` / `throw` 通過哪些方法來改善軟件質量? * [17.2] 如何處理構造函數的失敗? * [17.3] 如何處理析構函數的失敗? * [17.4] 如果構造函數會拋出異常,我該怎樣處理資源? * [17.5] 當別人拋出異常時,我如何改變字符數組的字符串長度來防止內存泄漏? ## 17.1 `try` / `catch` / `throw` 通過哪些方法來改善軟件質量? 通過排除使用`if`語句的一個理由。 代替 `try` / `catch` / `throw` 的通常做法是返回一個返回代碼(有時稱為錯誤代碼),調用者通過諸如`if`的條件語句明確地測試。例如,`printf()`, `scanf()` 和 `malloc()`就是這樣工作的:調用者被假定為會測試返回值來判斷函數是否成功。 盡管返回代碼技術有時是最適當的錯誤處理技術,但會產生增加不必要的`if`語句這樣的令人討厭的效果。 * **質量降級:**眾所周知,條件語句可能包含的錯誤大約十倍于其他類型的語句。因此,在其他都相同時,如果你能從代碼中消除條件語句,你會得到更健壯的代碼。 * **推遲面市:**由于條件語句是分支點,而它們關系到白盒法測試時的測試條件的個數,因此不必要的條件語句會增加測試的時間總量。如果你沒有走過每個分支點,那么你的代碼中就會有在測試中沒有被執行過的指令,直到用戶/客戶發現它,那就糟了。 * **增加開發成本:**不必要的控制流程的復雜性增加了尋找bug,修復bug,和測試的工作。 因此,相對于通過返回代碼和`if`來報告錯誤,使用`try` / `catch` / `throw`所產生更少有bug,更低的開發成本和更快面市的代碼。當然,如果你的團隊沒有任何使用`try` / `catch` / `throw`的經驗,你也許想先在一個玩具性的項目上使用一下,以便確定你明白正在做的事情——在把武器拿上真槍實彈的前線前,總應該演練一下吧。 ## 17.2 如何處理構造函數的失敗? 拋出一個異常。 構造函數沒有返回類型,所以返回錯誤代碼是不可能的。因此拋出異常是標記構造函數失敗的最好方法。 如果你沒有或者不愿意使用異常,這里有一種方法。如果構造函數失敗了,構造函數可以把對象帶入一種“僵尸”狀態。你可以通過設置一個內部狀態位使對象就象死了一樣,即使從技術上來說,它仍然活著。然后加入一個查詢(“檢察員”)成員函數,以便類的用戶能夠通過檢查這個“僵尸位”來確定對象是真的活著還是已經成為僵尸(也就是一個“活著的死對象”)。你也許想有另一個成員函數來檢查這個僵尸位,并且當對象并不是真正活著的時候,執行一個 no-op(或者是更令人討厭的如 `abort()`)。這樣做真的不漂亮,但是如果你不能(或者不想)使用異常的話,這是最好的方法了。 ## 17.3 如何處理析構函數的失敗? 往log文件中寫一個消息。或打電話給Tilda舅媽。但不要拋出異常! 以下是為什么(扣好你的安全帶): C++的規則是你絕對不可以在另一個異常的被稱為“棧展開(stack unwinding)”的過程中時,從析構函數拋出異常。舉例來說,如果某人寫了`throw?Foo()`,棧會被展開,以至`throw?Foo()`和 `}?catch?(Foo?e)?{` 之間的所有的棧頁面被彈出。這被稱為_棧展開(statck unwinding)_ 在棧展開時,棧頁面中的所有的局部對象會被析構。如果那些析構函數之一拋出異常(假定它拋出一個`Bar`對象),C++運行時系統會處于無法決斷的境遇:應該忽略`Bar`并且在`}?catch?(Foo?e)?{` 結束?應該忽略`Foo`并且尋找 `}?catch?(Bar?e)?{`?沒有好的答案——每個選擇都會丟失信息。 因此C++語言擔保,當處于這一點時,會調用`terminate()`來殺死進程。突然死亡。 防止這種情況的簡單方法是_不要從析構函數中拋出異常_。但如果你真的要聰明一點,你可以說_當處理另一個異常的過程中時,不要從析構函數拋出異常_。但在第二種情況中,你處于困難的境地:析構函數本身既需要代碼處理拋出異常,還需要處理一些“其他東西”,調用者沒有當析構函數檢測到錯誤時會發生什么的擔保(可能拋出異常,也可能做一些“其他事情”)。因此完整的解決方案非常難寫。因此索性就做一些“其他事情”。也就是,_不要從析構函數中拋出異常_。 當然,由于總有一些該規則無效的境況,這些話不應該被“引證”。但至少99%的情況下,這是一個好規則。 ## 17.4 如果構造函數會拋出異常,我該怎樣處理資源? 對象中的每個數據成員應該清理自己。 如果構造函數拋出異常,對象的析構函數將不會運行。如果你的對象需要撤銷一些已經做了的動作(如分配了內存,打開了一個文件,或者鎖定了某個信號量),這些需要被撤銷的動作必須被對象內部的一個數據成員記住。 例如,應該將分配的內存賦給對象的一個“智能指針”成員對象`Fred`,而不是分配內存給未被初始化的`Fred*` 數據成員。這樣當該智能指針消亡時,智能指針的析構函數將會刪除`Fred`對象。標準類`auto_ptr`就是這種“智能指針”類的一個例子。你也可以寫你自己的引用計數智能指針。你也可以用智能指針來指向磁盤記錄或者其它機器上的對象。 ## 17.5 當別人拋出異常時,我如何改變字符數組的字符串長度來防止內存泄漏? 如果你要做的確實需要字符串,那么不要使用`char`數組,因為數組會帶來麻煩。應該用一些類似字符串類的對象來代替。 例如,假設你要得到一個字符串的拷貝,隨意修改這個拷貝,然后在修改過的拷貝的字符串末尾添加其它的字符串。字符數組方法將是這樣: ``` ?void?userCode(const?char*?s1,?const?char*?s2) ?{ ???//?制作s1的拷貝: ???char*?copy?=?new?char[strlen(s1)?+?1]; ???strcpy(copy,?s1); ???//?現在我們有了一個指向分配了的自由存儲的內存的指針, //?w我們需要用一個try塊來防止內存泄漏: ???try?{ ?????//?...?n現在我們隨意亂動這份拷貝... //?將s2 添加到被修改過的 copy 末尾: //?...?[在此處重分配 copy]?... ?????char*?copy2?=?new?char[strlen(copy)?+?strlen(s2)?+?1]; ?????strcpy(copy2,?copy); ?????strcpy(copy2?+?strlen(copy),?s2); ?????delete[]?copy; ?????copy?=?copy2; ?????//?...?最后我們再次隨意亂動拷貝... ???}?catch?(...)?{ ?????delete[]?copy;???//?得到一個異常時,防止內存泄漏 ?????throw;???????????//?重新拋出當前的異常 ???} ???delete[]?copy;?????//?沒有得到異常時,防止內存泄漏 ?} ``` 象這樣使用`char*`s是單調的并且容易發生錯誤。為什么不使用一個字符串類的對象呢?你的編譯器也許提供了一個字符串類,而且它可能比你自己寫的`char*`s更快,當然也更簡單、更安全。例如,如果你使用了標準化委員會的字符串類`std::string`,你的代碼看上去就會象這樣: ``` ?#include?<string>???????????//?讓編譯器找到 std::string 類 ?void?userCode(const?std::string&?s1,?const?std::string&?s2) ?{ ???std::string?copy?=?s1;????//?制作s1的拷貝 //?...?現在我們隨意亂動這份拷貝... ???copy?+=?s2;???????????????//?A將 s2 添加到被修改過的拷貝末尾 //?...?最后我們再次隨意亂動拷貝... ?} ``` 函數體中總共只有兩行代碼,而前一個例子中有12行代碼。節省來自內存管理,但也有一些是來自于我們不必先式的調用`str_xxx_()`例程。這里有一些重點: * 由于`std::string`自動處理了內存管理,當增長字符串時,我們不需要先式地寫任何分配內存的代碼。 * 由于`std::string`自動處理了內存管理,在結束時不需要 `delete[]` 任何東西。 * 由于`std::string`自動處理了內存管理,在第二個例子中不需要 `try` 塊,即使某人會在某處拋出異常。
                  <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>

                              哎呀哎呀视频在线观看