Effective C++ 條款08_不止於此
別讓異常逃離解構函式
C++ 並不禁止解構函式吐出異常,但它也不鼓勵這樣做。
考慮下面程式碼:
class Widget {
public:
...
~Widget( ) {...} // 假設這裡吐出一個異常
};
void soSomething()
{
std::vector<Widget> vec;
...
} // vec 在這裡被自動銷燬
當 vector vec 被銷燬,它有責任銷燬其內含的所有 Widgets。假設 vec 內含10個Widgets,而在析構第一個元素期間,有一個異常被丟擲。其他九個 Widgets 還是應該被銷燬(否則它們儲存的任何資源都會發生洩漏),因此 vec 應該呼叫它們各個解構函式。但假設在那些呼叫期間,第二個 Widget 解構函式又丟擲異常。現在有兩個同時作用的異常,這對 C++ 而言太多了。在兩個異常同時存在的情況下,程式若不是結束就是導致不明確行為。本例會導致不明確行為。
這很容易理解,但如果你的解構函式必須執行一個動作,而該動作可能會在失敗是丟擲異常,該咋辦呢?舉個例子,假設你使用一個 class 來負責資料庫連線:
class DBConnection {
public:
...
static DBConnection create(); //返回 DBConnection 物件
void close(); // 關閉聯機;失敗則丟擲異常
};
- 為了確保使用者不忘記在 DBConnection 物件身上呼叫 close() ,我們可以這樣做,建立一個用來管理 DBConnection 資源的 class,並在其解構函式中呼叫 close。
程式碼如下:
class DBConn {
DBConnection db;
public:
...
~DBConn(){
db.close();
}
};
這樣便允許使用者寫出這樣的程式碼:
{ // 開啟一個區塊(block)。
DBConn dbc(DBConnection::create()); // 建立 DBConnection 物件並交給 DBConn 物件以便管理。
// 通過 DBConn 的介面使用 DBConnection 物件。
// 在區塊結束點,DBConn 物件被銷燬,因而自動為 DBConnection 物件呼叫 close
...
}
只要呼叫 close 成功,一切都美好,但如果該呼叫導致異常,DBConn 解構函式會傳播該異常,也就是允許它離開這個解構函式。那樣就會造成問題,因為那是丟擲了難以駕馭的麻煩。
下面給出兩種辦法解決這個問題
- 如果 close 丟擲異常就結束程式。通常通過呼叫 abort 完成:
DBConn::~DBConn( ){
try { db.close(); }
catch ( ... ){
// 製作運轉記錄,記下對 close 的呼叫失敗
std::abort(); // 使用 abort 可以搶先制 “ 不明確 ” 行為於死地,也就是強迫結束程式
}
}
- 吞下因呼叫 close 而發生的異常:
DBConn::~DBConn( ){
try { db.close(); }
catch ( ... ){
// 製作運轉記錄,記下對 close 的呼叫失敗
}
}
一般而言,這樣做是一個壞主意,因為它壓制了 “ 某些動作失敗 ” 的重要資訊!然而有時候吞下異常也比負擔 “ 草率結束程式 ” 或 “ 不明確行為帶來的風險 ” 好。
顯然這些方法都不具有吸引力。問題在於兩者都無法對 “ 導致 close丟擲異常 ” 的情況做出反應。
一種較佳策略是重新設計 DBConn 介面,使使用者有機會對可能出現的問題做出反應:
class DBConn {
public:
void close (){ // 供使用者使用的新函式
db.close();
closed = true;
}
~DBConn(){
if(!closed) {
try { // 關閉連線(如果使用者不那麼做(呼叫close())的話)
db.close();
}
catch(...){
// 製作運轉記錄,記下對 close 的呼叫失敗
...
}
}
}
private:
DBConnection db;
bool closed;
};
把呼叫 close 的責任從 DBConn 解構函式手上移到 DBConn 使用者手上,讓使用者有機會第一手處理問題。
最後請記住:
- 解構函式絕對不要吐出異常。如果一個被解構函式呼叫的函式可能丟擲異常,解構函式應該捕捉任何異常,然後吞下它們(不傳播)或結束程式。
- 如果使用者需要對某個操作函式執行期間丟擲的異常作出反應,那麼 class 應該提供一個普通函式(而非在解構函式中)執行該操作。
相關文章
- More Effective C++ 條款4 (轉)C++
- More Effective C++ 條款19 (轉)C++
- More Effective C++ 條款6 (轉)C++
- More effective C++ 條款14 (轉)C++
- More Effective C++ 條款15 (轉)C++
- More Effective C++ 條款2 (轉)C++
- More Effective C++ 條款3 (轉)C++
- More Effective C++ 條款11 (轉)C++
- More effective C++ 條款13 (轉)C++
- More effective C++ 條款12 (轉)C++
- More Effective C++ 條款5 (轉)C++
- More Effective C++ 條款一 (轉)C++
- More Effective C++ 條款17 (轉)C++
- More Effective C++ 條款18 (轉)C++
- More Effective C++ 條款20 (轉)C++
- More Effective C++ 條款21 (轉)C++
- More Effective C++ 條款22 (轉)C++
- More Effective C++ 條款23 (轉)C++
- More Effective C++ 條款24 (轉)C++
- More Effective C++ 條款25 (轉)C++
- More Effective C++ 條款7 (轉)C++
- More Effective C++ 條款8 (轉)C++
- More Effective C++ 條款28(中) (轉)C++
- More effective c++ 條款10(上) (轉)C++
- More effective c++ 條款10(下) (轉)C++
- More Effective C++ 條款27(下) (轉)C++
- More Effective C++ 條款28(上) (轉)C++
- More Effective C++ 條款28(下) (轉)C++
- More Effective C++ 條款26(下) (轉)C++
- Effective Modern C++ 系列之 條款2: autoC++
- Effective c++條款11:在operator=中處理“自我賦值”C++賦值
- SAP軟體的強大“遠不止於此”
- [讀書筆記][effective C++]條款30-inline的原理筆記C++inline
- C++::My Effective C++C++
- 新一代Mac晶片即將來臨,蘋果野心不止於此Mac晶片蘋果
- effective C++ : CHAPTER 8C++APT
- Effective C++筆記C++筆記
- 【Effective Modern C++】索引C++索引