| 級別 | 保證內容 |
|---|---|
| 基本保證 | 異常後對象處於「未知但有效」狀態(不變式完整)。 |
| 強保證 | 操作具備事務性:要么成功達到目標狀態,要么狀態不變(回滾)。 |
| 不拋異常 | 操作保證不拋出異常。析構、釋放、swap 必須達此級別。 |
錯誤模式:
Type* p = new Type;
try { ... } catch(...) { delete p; throw; }
正確模式:
auto_ptr<Type> p(new Type); // 或 unique_ptr/shared_ptr
do_something(); // 異常自動釋放,無需 try-catch
原則:不關心/無法處理的異常,不要捕獲。大多數異常安全代碼無 try-catch。
成員為原始指針時:
Type() : m_a(new TypeA), m_b(new TypeB) {} // 危險:new B 失敗導致 A 洩漏
改進:智能指針成員(shared_ptr/scoped_ptr)自動管理:
shared_ptr<TypeA> m_a; shared_ptr<TypeB> m_b;
Type() : m_a(new TypeA), m_b(new TypeB) {} // 安全
// 鎖
struct scoped_lock {
explicit scoped_lock(Lock& l): m_l(l) { m_l.lock(); }
~scoped_lock() { m_l.unlock(); }
Lock& m_l;
};
scoped_lock guard(a_lock); // 自動加解鎖
通用模板 pair_guard 適配任意成對函數(配合 boost::bind/std::bind)。
// 字符串賦值基本保證版
myalloc pool(m_data);
pool.deallocate(m_data); // 進入非法
m_data = nullptr; // 立即恢復合法(空串)
if (!rsh.empty()) {
m_data = pool.allocate(rsh.size()+1);
never_failed_copy(m_data, rsh.m_data, rsh.size()+1);
}
// 字符串賦值強保證版
myalloc pool(m_data);
char* tmp = nullptr;
if (!rsh.empty()) {
tmp = pool.allocate(rsh.size()+1);
never_failed_copy(tmp, rsh.m_data, rsh.size()+1); // 先構建目標狀態
}
swap(tmp, m_data); // 原子切換到目標狀態(swap 不拋異常)
pool.deallocate(tmp); // 釋放舊資源
關鍵:在可能失敗的階段不修改對象,構建臨時目標狀態;在不拋異常階段(swap)提交。
tmp = range;
for (itr = tmp.begin(); itr != tmp.end(); ++itr) itr->do_something();
swap(tmp, range);
需完整複製範圍,性能開銷大,視情況權衡。
throw new Exception(洩漏),拋值 throw Exception()。catch(const std::exception& e)。throw()),編譯器不強制執行,C++11 棄用。