Effective C++ 條款08:別讓異常逃離解構函式
阿新 • • 發佈:2021-06-30
解構函式丟擲異常會有什麼後果
假設現在我們有一個類Widget
class Widget{
public:
...;
~Widget() {
// 這裡丟擲異常了
...;
}
};
然後我們建立一個Widget型別的的vector陣列
int main(){
std::vector<Widget> v;
}
假設現在v的生命週期結束了,它會析構掉裡面的所有Widget物件
然而此時,析構第一個Widget物件時丟擲了異常。為了防止記憶體洩露,我們依然要保證後面的其他所有Widget物件被析構。
但很不幸的是,析構第二個Widget時又丟擲了一個異常。那麼此時編譯器丟擲了兩個異常。對於c++而言丟擲兩個異常是非常危險的,這會導致程式執行不明確行為。具體為什麼在More Effective裡有講到,這裡只需知道c++不喜歡解構函式丟擲異常即可
但是解構函式必須執行一個可能丟擲異常的動作怎麼辦
書上告訴我們:交給使用者
考慮這個資料庫管理類
class DBConnection;
class DBConn{
public:
~DBConn(){
db.close();
}
private:
DBConnection db;
};
DBConnection是一個數據庫連線類,它有一個close方法,這個方法會丟擲異常。我們希望設計一個新的類DBConn來管理資料庫的連線與釋放。也就是當DBConn物件析構時,自動關閉資料庫連線。
但是很遺憾close這個方法會丟擲異常。那麼我們需要重新設計介面,提供一個普通函式(非析構)給使用者,然他自己釋放。如果使用者也忘了,那才交給解構函式釋放。如果這時候還出問題了,那就聽天由命吧。不是所有的問題都有完美的解決方法的!
class DBConn{ public: // 給使用者提供的普通函式來關閉連線 void close(){ db.close(); closed = true; } ~DBConn(){ if (!closed) { // 如果使用者也忘了,就讓解構函式來 try{ db.close(); } catch (...) { // 很不幸析構時丟擲了異常 // 要麼記錄下來,結束程式 std::abort() // 要麼吞掉 } } } private: DBConnection db; bool closed; }