Image *bgImage; // current background image int imageChanges; // # of times image has been changed }; 考慮這個(gè) PrettyMenu 的 changeBackground 函數(shù)的可能的實(shí)現(xiàn):
作為一個(gè)一般性的規(guī)則,你應(yīng)該提供實(shí)際可達(dá)到的最強(qiáng)力的保證。從異常安全的觀點(diǎn)看,不拋出的函數(shù)(nothrow functions)是極好的,但是在 C++ 的 C 部分之外部不調(diào)用可能拋出異常的函數(shù)簡(jiǎn)直就是寸步難行。使用動(dòng)態(tài)分配內(nèi)存的任何東西(例如,所有的 STL 容器)假如不能找到足夠的內(nèi)存來(lái)滿足一個(gè)請(qǐng)求,在典型情況下,它就會(huì)拋出一個(gè) bad_alloc 異常。只要你能做到就提供不拋出保證,但是對(duì)于大多數(shù)函數(shù),選擇是在基本的保證和強(qiáng)力的保證之間的。
void someFunc() { ... // make copy of local state f1(); f2(); ... // swap modified state into place } 很明顯,假如 f1 或 f2 低于強(qiáng)力異常安全,someFunc 就很難成為強(qiáng)力異常安全的。例如,假設(shè) f1 僅提供基本保證。為了讓 someFunc 提供強(qiáng)力保證,它必須寫(xiě)代碼在調(diào)用 f1 之前測(cè)定整個(gè)程序的狀態(tài),并捕捉來(lái)自 f1 的所有異常,然后恢復(fù)到最初的狀態(tài)。
即使 f1 和 f2 都是強(qiáng)力異常安全的,事情也好不到哪去。假如 f1 運(yùn)行完成,程序的狀態(tài)已經(jīng)發(fā)生了毫無(wú)疑問(wèn)的變化,所以假如隨后 f2 拋出一個(gè)異常,即使 f2 沒(méi)有改變?nèi)魏螙|西,程序的狀態(tài)也已經(jīng)和調(diào)用 someFunc 時(shí)不同。
問(wèn)題在于副作用。只要函數(shù)僅對(duì)局部狀態(tài)起作用(例如,someFunc 僅僅影響調(diào)用它的那個(gè)對(duì)象的狀態(tài)),它提供強(qiáng)力保證就相對(duì)輕易。當(dāng)函數(shù)的副作用影響了非局部數(shù)據(jù),它就會(huì)困難得多。例如,假如調(diào)用 f1 的副作用是改變數(shù)據(jù)庫(kù),讓 someFunc 成為強(qiáng)力異常安全就非常困難。一般情況下,沒(méi)有辦法撤銷(xiāo)已經(jīng)提交的數(shù)據(jù)庫(kù)變化,其他數(shù)據(jù)庫(kù)客戶可能已經(jīng)看見(jiàn)了數(shù)據(jù)庫(kù)的新?tīng)顟B(tài)。
請(qǐng)答應(yīng)我回到懷孕的話題。一個(gè)女性或者懷孕或者沒(méi)有。局部懷孕是絕不可能的。與此相似,一個(gè)軟件或者是異常安全的或者不是。沒(méi)有像一個(gè)局部異常安全的系統(tǒng)這樣的東西。一個(gè)系統(tǒng)即使只有一個(gè)函數(shù)不是異常安全的,那么系統(tǒng)作為一個(gè)整體就不是異常安全的,因?yàn)檎{(diào)用那個(gè)函數(shù)可能發(fā)生泄漏資源和惡化數(shù)據(jù)結(jié)構(gòu)。不幸的是,很多 C++ 的遺留代碼在寫(xiě)的時(shí)候沒(méi)有留意異常安全,所以現(xiàn)在的很多系統(tǒng)都不是異常安全的。它們混合了用非異常安全(exception-unsafe)的方式書(shū)寫(xiě)的代碼。