條款08 別讓異常逃離解構函式,條款08逃離函數

來源:互聯網
上載者:User

條款08 別讓異常逃離解構函式,條款08逃離函數

結論

1. 解構函式絕對不要吐出異常。如果一個被解構函式調用的函數可能拋出異常,解構函式應該能夠捕捉任何異常,然後吞下他們(不傳播)或結束程式。

2. 如果客戶需要對某個操作函數運行期間拋出的異常做出反應,那麼class應該提供一個普通函數(而不是在解構函式中)執行該操作。


        C++ 不禁止但不鼓勵從解構函式引發異常。考慮:

class Widget {public:   ...   ~Widget() { ... } // 假設這裡可能吐出一個異常};void doSomething(){   std::vector<Widget> v;   ...} // v在這裡被自動銷毀
        當 vector v 被析構時,它有責任析構它包含的所有 Widgets。但假設在那些調用期間,先後有兩個Widgets拋出異常,對於 C++ 來說,這太多了。在兩個異常同時存在的情況下,程式若不是結束執行就是導致不明確行為。在本例中將導致不明確行為,使用標準庫的任何其他容器(如list,set)或TR1的任何容器甚至array,也會出現相同情況。C++ 不喜歡解構函式吐出異常。


        如果你的解構函式需要執行一個可能失敗而拋出一個異常的操作,該怎麼辦呢?假設使用一個class負責資料庫連接,為了確保客戶不會忘記在 DBconnection對象上調用 close(),一個合理的想法是建立一個用來管理DBConnection資源的類,並在其解構函式中調用close:

class DBConn { // 這個類用來管理DBConnection對象public:        // objects  ...  ~DBConn()     // 確保資料庫連接總是會被關閉   {        db.close();}private:   DBConnection db;};
它允許客戶像這樣編程:

{ // 開啟一個區塊(block)   DBConn dbc(DBConnection::create());       // 建立DBConnection並交給DBConn對象以便管理   ... // 通過DBConn的介面使用DBConnection對象}     //在區塊結束點,DBConn對象被銷毀,因而自動為DBConnection對象調用close
        只要調用 close 成功,一切都美好。但是如果這個調用導致一個異常,DBConn 的解構函式將傳播那個異常,也就是允許它離開解構函式。這就產生了問題,因為解構函式拋出了一個燙手的山芋。


有兩個主要的方法避免這個麻煩

(1)Terminatethe program:如果 close 拋出異常就終止程式,一般是通過調用 abort。

DBConn::~DBConn(){try { db.close(); }   catch (...) {        製作運轉記錄,記下對close的調用失敗;        std::abort();   }}
       它有一個好處是:阻止異常從解構函式中傳播出去(那會導致不明確的行為)。也就是說,調用 abort 可以預先制“不明確行為”於死地。
(2)Swallowthe exception:吞下因調用close而發生的異常。在此例中將在第一種方法下去掉abort那句語句。

         通常,將異常吞掉是個壞主意,因為它隱瞞了“某些動作失敗”的重要訊息!然而,有些時候,吞下異常比冒程式過早終止或不明確行為的風險更可取。程式必須能夠在遭遇到一個錯誤並忽略之後還能繼續可靠地運行,這才能成為一個可行的選擇。

以上方法的問題都在於兩者無法對引起 close 拋出異常的情況做出回應。


一個更好的策略是重新設計 DBConn 的介面,以使客戶有機會對可能發生的問題做出回應。

class DBConn {public:    ...    void close() // 供客戶使用的新函數    {       db.close();       closed = true;    }    ~DBConn()    {       if (!closed) {           try {               db.close(); // 關閉串連(如果客戶不那麼做的話)           }            catch (...) { // 如果關閉動作失敗,記錄下來並結束程式或吞下異常               製作運轉記錄,記下對close的調用失敗;               ...           }        }    }    private:        DBConnection db;        bool closed;};
        這樣把調用 close 的責任從 DBConn 的解構函式移交給 DBConn 的客戶(同時在 DBConn 的解構函式中仍內含一個“雙保險調用”)。如果某個操作可能在失敗時拋出異常,而又存在某種需要必須處理該異常,那麼這個異常必須來自解構函式以外的某個函數。這是因為解構函式)引發異常是危險的,永遠都要冒著程式過早終止或 不明確行為的風險。在本例中,讓客戶自己調用 close 並不是強加給他們的負擔,而是給他們一個處理錯誤的機會。他們可以忽略它,依靠 DBConn 的解構函式去調用 close。如果真有錯誤發生,close的確拋出異常而且DBConn吞下該異常或結束程式,客戶沒有立場抱怨,畢竟他們曾有機會第一手處理問題,而他們選擇了放棄。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.