在演算法與程式的開發過程中,如果寫下每一行時都要考慮這個函式可能傳回的error,不僅讓撰寫變慢,而且本來流暢的思緒被卡住。如果寫程式時,先只關注預期的程式路徑,把catch放在程式最外圈。經過不斷的測試之後,再把錯誤頻繁發生的區段用try包,這樣開發才快。我這想法比較像test-driven,也許有人會認為「測試引導開發」會埋下更大的錯誤,到了很晚才爆發。但我認為很多事情你自己不先快速走一遭,你永遠無法知道會發生什麼。你在寫程式前預期會發生的錯誤沒有發生,反倒是「不可能」、「沒想到」的錯誤發生。如果你這個第一次快速走一遭的「預習」太晚完成,將會拖累整個開發的進度。
Raymond Chen的Blog: Old New Things中也說Exception Handling是Cleaner, more elegant, and wrong。但他舉的例子不好。在這例子就算是逐行用if-goto來檢查,寫的不好一樣有問題。C++ exception最大的問題是,許多人誤以為只要最後有用catch接到例外,try區塊中曾經配置過的memory、resource都會自動釋放,所有的狀態都會回復到try發生之前。
這是錯誤的!如果沒有達到David Abrahams所稱的Exception Guarantees,不管你是用if亦或exception來寫,都會有問題。狀態無法rollback成try之前,resource沒被釋放。
想像有一個提款機轉帳的函式,先從sender帳戶扣款,再給recipient帳戶加上款項。
void TransferMoney(Person& sender, Person& recipient, int money){
SubtractAccount(sender, money);
AddAccount(recipient, money);
}
如果SubtractAccount()執行成功,但要執行下一行時,連線出問題,那sender就虧大了,白白損失一筆錢。如果說你討厭exception,你用if來寫程式:
HRESULT TransferMoney(Person& sender, Person& recipient, int money){
HRESULT hr=S_OK;
if((hr=SubtractAccount(sender, money)) != S_OK)
return hr;
if((hr=AddAccount(recipient, money))!=S_OK)
return hr;
return hr;
}
這個程式執行一半,出了錯誤,直接return error,雖然從程式碼中可直接看出錯誤處理的路徑,但並不符合Abrahams' Guarantees!所有帳款資料庫的狀態沒有回復到函式執行之前。這個問題只要C++語法中沒有提出transactional programming解決方案前,不管是用if還是exception,錯誤處理都是要逐行細心考慮。
No comments:
Post a Comment