effective C++ 條款 8:別讓異常逃離解構函式

來源:互聯網
上載者:User

c++並不禁止解構函式拋出異常,但它不鼓勵這樣做:

class Widget{

public:

    …

    ~Widget();  //假設可能拋出異常

};

void doSomething()

{

    std::vector<Widget> v;

    …

}

當vector v被銷毀,它有責任銷毀其內含的所有Widgets。假設在析構第一個元素期間,有個異常被拋出,其他還是應該被銷毀,因此v應該調用它們各個解構函式。但假設第二個Widget解構函式由拋出異常。現在,有兩個同時作用的異常,這對c++來說太多了,兩個異常同時存在的情況下,程式若不是結束執行,就是導致不明確的行為。即使並不是使用容器或者arrays, 程式也可能過早結束或出現不明確行為。c++不喜歡解構函式拋出異常。

假如你的解構函式必須執行一個動作,而該動作可能會在失敗時拋出異常,該怎麼辦?如:有個負責資料庫連接的class:

class DBConnection{

public:

    …

   static DBConnection create();

    void close();   //關閉聯機, 失敗則拋出異常。

};

為了客戶不忘記在DBConnection對象上調用close(),一個合理的想法是建立一個用來管理DBConnection資源的class,並在解構函式中調用close():

class DBConn{

public:

    …

    ~DBConn()

    {

        db.close();

    }

private:

    DBConnection db;

};

這使客戶可以寫這樣的代碼:

{

    DBConn dbc(DBConnection::create());

    …

} // 區塊結束後,DBConn對象被銷毀,因而自動為DBConnection對象調用close。

如果調用close成功,一切都美好,但如果該調用導致異常,DBConn解構函式會傳播該異常,也就是允許它離開這個解構函式。那會造成問題,因為那就是拋出了難以駕馭的麻煩。

有兩個方法可以避免這個問題,DBConn的解構函式可以:

1 如果close拋出異常就結束。通常通過調用abort完成。

DBConn::~DBConn()

{

    try {db.close();}

    catch (…){

        記下對close的調用失敗;

        std::abort();

    }

}

如果程式遭遇一個“於析構期間發生的錯誤”後無法繼續執行, “強迫結束程式”是個合理選項。就是說調用abort可以搶先制“不明確行為”於死地。

2 吞下因調用close發生的異常。

DBConn::~DBConn()

{

    try {db.close();}

    catch (…){

        記下對close的調用失敗;

    }

}

一般而言,將異常吞掉是個壞主意, 因為它壓制了“某些動作失敗”的重要訊息!然而有時候“吞下異常”比負擔“草率結束程式”或“不明確行為帶來的風險”好。為讓這成為一個可行方案,程式必須能夠繼續可靠的執行,即使在遭遇並忽略一個錯誤之後

這些辦法都沒什麼吸引力,問題在於兩者都無法對“導致close拋出異常”的情況作出反應。

一個較佳的策略是重新設計DBConn介面,使其客戶有機會對可能出現的問題作出反應。例如DBConn可以自己提供一個close函數,因而賦予客戶一個機會得以處理“因該操作而發生的異常”。DBConn也可以追綜其所管理的DBConnection是否已經關閉,並在未關閉的情況下由其解構函式關閉之。這可以防止遺失資料庫連接。然而如果DBConn解構函式調用close失敗,我們又將退回到“強迫結束程式”或“吞下異常”的老路。

class DBConn{

public:

    …

    void close()

    {

         db.close();

         closed = true;

    }

    ~DBConn()

    {

        if (!closed){

            try{ db.close();}

            catch(…){製作運轉記錄,記下對close調用的失敗;

                …

            }

        }

    }

private:

    DBConnection db;

    bool closed;

};

如果某個操作可能在失敗時拋出異常,而又存在某種需要必須處理該異常,那麼這個異常必須來自解構函式以外的某個函數。因為解構函式拋出異常就是危險,總會帶來“程式過早結束”或“發生不明確行為”的風險。由客戶自己調用close並不會對他們帶來負擔, 而是給他們一個處理錯誤的機會,否則他們沒機會響應。如果他們不認為這個機會有用(或許他們堅信不會有錯誤發生),可以忽略它,依賴DBConn的解構函式來調用close。如果有錯誤發生,close的確拋出異常--並且DBConn吞下該異常或結束程式,客戶沒有立場抱怨,畢竟他們曾有機會第一手處理問題,但他們選擇了放棄。

解構函式絕對不能拋出異常,如果一個解構函式調用的函數可能拋出異常,解構函式應該能捕捉任何異常,然後吞下它(不傳播)

或結束程式。

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

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.