析構函數絕不能拋出異常。若析構函數拋出異常,程序將面臨「過早終止」或「未定義行為」的風險。這是 C++ 與 Java/C# 等語言的重大差異之一。
假設有一個數據庫連接類 DBConnection,其 close() 方法在失敗時會拋出異常:
class DBConnection {
public:
static DBConnection create();
void close(); // 失敗時拋出異常
};
創建管理類 DBConn,提供顯式 close() 供調用者主動處理異常,析構函數中兜底調用並捕獲所有異常:
class DBConn {
public:
DBConn() : db(DBConnection::create()), closed(false) {}
~DBConn() {
if (!closed) {
try {
db.close(); // 關閉連接
} catch (...) {
// 方案一:記錄日誌並終止程序
log_error("DBConnection close failed in destructor");
std::abort();
// 方案二:吞噬異常(僅記錄,不傳播)
// log_error("DBConnection close failed, exception swallowed");
}
}
}
void close() { // 供客戶顯式調用
db.close();
closed = true;
}
private:
DBConnection db;
bool closed;
};
// 錯誤示範:析構函數直接拋出異常
class BadDBConn {
public:
~BadDBConn() {
db.close(); // 若拋出異常且正在堆疊展開期間,程序直接 terminate
}
private:
DBConnection db;
};
std::terminate()☐ 所有析構函數標記noexcept(C++11 起默認)
☐ 析構函數內部調用的可能拋出異常的函數均用try-catch包裹
☐ 提供顯式close()/release()供調用者主動釋放資源
☐ 異常被捕獲後至少記錄關鍵上下文信息
「析構函數不拋出異常」不是建議,是鐵律。通過 RAII 包裝器提供顯式釋放介面,在析構函數中兜底捕獲並記錄/終止,是處理可能失敗的清理操作的標準模式。