Effective C++ 第二十九條 Strive for exception-safe code.

為“異常安全”而努力是值得的
我們在書寫函數(shù)的時候難免會碰到異常,但是碰到異常我們應(yīng)當保證兩點:
不泄漏任何資源
不允許數(shù)據(jù)破壞
下面有一段在安全性上非常糟糕的代碼
在這段代碼中,一旦 new Image (imgSrc) 出現(xiàn)異常,后續(xù)的 unlock 操作就不會執(zhí)行,于是互斥器就永遠鎖住了。而且原有的 bgImage 被刪除,imageChanges 也被改變,然而沒有新的圖像被替換。
一般來說,代碼越少就越好,因為語句越少,出錯的可能就越少。,一旦有一句改變,被誤解的機會也少。
我們在 第十三條 說過,使用資源管理類來管理資源可以避免資源泄漏這類事情發(fā)生,于是代碼變成了這樣
但是數(shù)據(jù)已經(jīng)可能被破壞,這一改變只解決了資源泄漏,沒有解決數(shù)據(jù)破壞。
在《Effective C++》中,定義三種異常安全函數(shù)的保證
基本承諾 如果異常被拋出,程序內(nèi)部任何事物仍然保持在有效狀態(tài)下,沒有任何對象和數(shù)據(jù)結(jié)構(gòu)會因此破壞。
強烈保證 如果異常被拋出,那么程序狀態(tài)不改變。要么執(zhí)行成功,如果不成功,程序也會恢復(fù)到調(diào)用該函數(shù)之前的狀態(tài)??梢哉J為是原子性。
不拋擲保證 承諾絕對不拋出異常,函數(shù)一定能完成原先承諾的功能。
除非你真的需要在應(yīng)用程序中泄漏資源和執(zhí)行過程中帶著被破壞的數(shù)據(jù)(比如測試代碼的時候),否則都應(yīng)該提供異常安全保證。
我們重新排列函數(shù)的語句
這里如果 new Image 執(zhí)行失敗,bgImage.reset 就不會執(zhí)行,保證了 bgImage 的安全,數(shù)據(jù)破壞被避免,但仍然沒有提供了強烈保證。因為參數(shù) imgSrc,如果 new Image 拋出異常,輸入流(istream)可能被移走,這個動作也是一種程序可見狀態(tài)的改變,所以只是基本異常安全保證。
前邊介紹了一種 copy and swap 的方式可以有效提高安全性至強烈安全,就是你把你要在函數(shù)中修改的部分 copy 一份,然后對 copy 的那一份進行操作,如果沒報錯就將該副本和原對象進行交換。
這樣就實現(xiàn)了強烈保證,但這一方法不是永遠有效,因為在一個函數(shù)中很多時候要調(diào)用其他函數(shù),如果其他函數(shù)不是強烈保證的異常安全函數(shù),就算你在該函數(shù)中保證也沒用。就好比本例子中,如果 swap 函數(shù)不是強烈保證的異常安全函數(shù),即使按照 copy and swap 的方法寫出來的函數(shù)也不是強烈保證的異常安全函數(shù),如果 swap 改變了狀態(tài),我們也無從下手。我們寫出來的異常安全函數(shù)的安全等級是整個函數(shù)中所有部分的安全等級中取安全等級最低為準。
總結(jié):
異常安全函數(shù)即使發(fā)生異常也不允許泄漏資源和數(shù)據(jù)結(jié)構(gòu)破壞。
強烈保證大部分時候可以通過 copy and swap 來實現(xiàn),但不總是奏效。
函數(shù)提高的“異常安全保證”通常最高只等于其所調(diào)用之各個函數(shù)的“異常安全保證”中的最弱者。