1. 程式人生 > 其它 >Effective C++ 條款08:別讓異常逃離解構函式

Effective C++ 條款08:別讓異常逃離解構函式

解構函式丟擲異常會有什麼後果

假設現在我們有一個類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;
}