作為一個一般性的規則,你應該提供實際可達到的最強力的保證。從異常安全的觀點看,不拋出的函數(nothrow functions)是極好的,但是在 C++ 的 C 部分之外部不調用可能拋出異常的函數簡直就是寸步難行。使用動態分配內存的任何東西(例如,所有的 STL 容器)假如不能找到足夠的內存來滿足一個請求,在典型情況下,它就會拋出一個 bad_alloc 異常。只要你能做到就提供不拋出保證,但是對于大多數函數,選擇是在基本的保證和強力的保證之間的。
void someFunc() { ... // make copy of local state f1(); f2(); ... // swap modified state into place } 很明顯,假如 f1 或 f2 低于強力異常安全,someFunc 就很難成為強力異常安全的。例如,假設 f1 僅提供基本保證。為了讓 someFunc 提供強力保證,它必須寫代碼在調用 f1 之前測定整個程序的狀態,并捕捉來自 f1 的所有異常,然后恢復到最初的狀態。
即使 f1 和 f2 都是強力異常安全的,事情也好不到哪去。假如 f1 運行完成,程序的狀態已經發生了毫無疑問的變化,所以假如隨后 f2 拋出一個異常,即使 f2 沒有改變任何東西,程序的狀態也已經和調用 someFunc 時不同。
問題在于副作用。只要函數僅對局部狀態起作用(例如,someFunc 僅僅影響調用它的那個對象的狀態),它提供強力保證就相對輕易。當函數的副作用影響了非局部數據,它就會困難得多。例如,假如調用 f1 的副作用是改變數據庫,讓 someFunc 成為強力異常安全就非常困難。一般情況下,沒有辦法撤銷已經提交的數據庫變化,其他數據庫客戶可能已經看見了數據庫的新狀態。
請答應我回到懷孕的話題。一個女性或者懷孕或者沒有。局部懷孕是絕不可能的。與此相似,一個軟件或者是異常安全的或者不是。沒有像一個局部異常安全的系統這樣的東西。一個系統即使只有一個函數不是異常安全的,那么系統作為一個整體就不是異常安全的,因為調用那個函數可能發生泄漏資源和惡化數據結構。不幸的是,很多 C++ 的遺留代碼在寫的時候沒有留意異常安全,所以現在的很多系統都不是異常安全的。它們混合了用非異常安全(exception-unsafe)的方式書寫的代碼。