C++ 引入了 Exception 機制,讓使用者可以用較彈性的方式處理
異常狀況。這種「強迫回報」的作法雖然把選擇權轉移給上層模組
,但卻帶來了另一個的問題。
這個問題就是,throw exception 的動作破壞了程式正常的流程,
例如:
void f()
{
char *p = new char[100];
...
if (...)
{
throw 1;
}
delete [] p;
}
在這個簡單的例子中,假如條件測試沒有通過,也就是說,f 拋
出異常的話(而且被上層模組處理掉了),那麼原本執行到右大
括號才離開函式 f 的行為,就發生了改變,變成執行到 throw
異常的那一行敘述就中斷了,直接返迴。換句話說,delete [] p
的動作沒有被執行到。
類似這種狀況,我們就說,函式 f 不滿足 Exception-Safety。
有人會說,那不要用 C++,用支援 GC(Garbage Collection)的
語言,例如 Java 或 C# 就沒這種問題了。
事實沒這麼簡單,再看另一個例子:
void g()
{
...
ThreadLock(Handle);
...
if (...)
{
throw 1;
}
...
ThreadUnlock(Handle);
}
函式 g 是一個多緒程式常見的範例。假設發生拋出異常(且被
上層模組處理掉),那這次就不只是 new 了沒有 delete 的小
事了(雖說是小事,但也很嚴重了)。這次如果只是當機,已經
算是很好的結果了,麻煩的是,萬一程式還繼續運作下去,卻間
接造成在其他的地方,出現了奇怪而無法預測的行為,就算想要
除錯,都根本不知從何著手。
簡單的說,一旦程式中有「資源配置」的動作,例如 new 一個
物件,鎖定一個關鍵區域(或多工器等執行緒物件),或其他任
何「配置、釋放」的動作,就要特別注意 throw exception 會
改變程式正常迴路的問題。
因此,要想達成 Exception Safety(異常安全)(意思是:即
使發生了異常,程式也能安全運行下去),其實是一個非常困難
的課題。有沒有考慮「異常安全」,會從最根本的層次,影響程
式碼的實作方式,也就是說,如果在最初設計的時候,沒有考量
到異常安全,最後要再追加,幾乎是不可能的(這時,重新設計
遠比改寫容易得多)。
要克服 exception 機制破壞正常程式迴路,所帶來的麻煩,最
基本(也是最有力、最重要)的手段,不是倚賴垃圾回收器,而
是徹底貫徹 RAII 的精神。但這是另一個課題了,這裏就不討論。
基於以上的原因,在一般應用上有兩個建議:
一、除非你很了解 C++ Exception 的機制,否則最好不要亂用
(不是不能用,而是不要到處亂用)。要知道,一旦函式中的
某個部份可能拋出異常,這段程式碼的迴路複雜度,就會以倍
數(甚至是級數)地增加。不當的運用,萬一出了狀況,等於
是自找麻煩。
二、除非有極端性能的考量,否則儘可能用 C++ 標準程式庫所
提供的各種工具。因為 C++ Standard 對於 Exception Safety
都有基本的要求,通常會比自己寫的程式碼安全得多。
--
※ 發信站: 批踢踢實業坊(ptt.cc)
◆ From: 59.120.214.120