深入分析異常機制!
我們知道C++和C最大的不同就是它更好的支援面向物件程式設計,封裝,多型,繼承,異常,名稱空間.通用型程式設計等等.
對於面向機制為什麼說是更好的支援哪.是因為用C同樣可以編寫面向物件程式設計.我們都知道C函式可以通過結構體和函式指標來設計一些低階的類.但C++提供的CLASS則更好的維護了資料的隱藏.而不是想C那樣任何資料都要通過函式來操作.C++是通過資料和方法的封裝來實現對資料的操作.
至於名稱空間更多的是突出模組化.把一些你所認為有理由放在一起的資料 類 函式 等等放在同一個名稱空間中,來體現模組的概念,並且也減少了名字衝突.
下面我們來說說異常.異常的存在不一定總是一件很好的事,它的存在影響著程式在空間和時間上的優勢.很多編譯器也就因此可以設定成不支援異常的編譯機制.但你要保證你的每個編譯單位都沒有用到異常,在連線時才能順利連線.
而異常的存在一定是有原因的.從類庫的設計到使用者使用類庫程式設計無不涉及到異常.異常是類庫給使用者自己當家做主的好方法.
只要你用TRY塊去檢查,CATCH字句是不會另你失望的.
我們拿最熟悉的OPERATOR NEW來說 ,如果分配記憶體失敗就會丟擲一個異常,BAD_ALLOC 如果你要為這件事負責就一定要有這樣的程式碼 CATCHI(BAD_ALLOC &) { DO SOMETHING }
當然如果你有這樣的程式碼set_new_handler(指向函式的指標) ; 那當OPERATOR NEW失敗的時候會先呼叫這個函式.
在這裡說一下 new handler 大概是這樣的定義的 typedef void (*new hander ) ()
也就是說new_handler是一個指向引數和返回值都為VOID 的函式指標.
所以當你 set_new_handler(指向函式的指標) ; 時 ,當operator new失敗的話會先呼叫該函式.
而set_new_handler()的原型應該大概是這樣 new hander set_new_hander (new handler)
返回的是之前的函式指標.所以當你自己寫operator new時要注意這點,用完後要恢復. 當你為set_new_handler 傳遞一個0時,就不呼叫任何函式 在operator new 失敗後.而直接丟擲bad_alloc異常.
接著從異常的傳遞來說一下,不論通過什麼方式來捕捉異常,異常丟擲的時候總會發生拷貝.
比如當我們有一個自己寫的異常類 error
當我們在TRY 中THROW 一個該類的異常 ,在CATCH時不論是catch(error)值傳遞 還是catch(error &) 異常丟擲時都會拷貝一個副本.值傳遞的話會用這個副本拷貝到catch(error)也就是說發生了兩次拷貝 如果是引用方式捕捉.則會直接用這個副本來初試化.我們知道含有隱式型別轉換的類做引數時,只能在接受值物件或者const 引用物件的函式時發生轉換.為什麼不能為非const引用物件發生一個隱式轉換大家應該很清楚,原因就是為轉換產生的是臨時物件.而在異常中非const 引用同樣可以將丟擲異常產生的這個臨時副本捕獲..雖然通過指標捕獲沒有發生指向物件的複製.但會引起型別的吻亂.
對於異常捕獲時能夠發生的型別轉換隻有2個 1 是 針對基類的CATCH可以處理派生類異常 所以一定要把派生類的CATCH寫在基類CATCH的上方.因為它只按順序來找 2是 所有指標都會轉換為VOID *
因為值方式捕捉會產生2個副本 所以大家最好用引用方式捕捉異常.
順便說一下.在CATCH中 如果THROW; 則是直接丟擲捕捉時的那個副本.而 THROW 物件; 則又會發生拷貝給呼叫者捕獲.
下面來說一下異常說明
就是在一個函式聲明後邊加上類似的程式碼 throw () 如果不加則是說該函式允許丟擲任何異常.如果throw()沒有任何引數則是說不允許丟擲任何異常.如果一個帶有throw()的函式呼叫了一個throw(typename)的函式,並且異常真的發生了,則會呼叫UNEXPECTED函式.預設的行為就是終止程式.
所以要避免呼叫者的異常說明範圍小於被呼叫函式的異常說明.
函式指標也一樣
在MORE EFFECTIVE C++中說到摸板和異常說明不要同時使用,希望大家能看一下.
最後一個避免的辦法就是保證為知型別的異常都會被捕獲
如果發生了一個異常說明中沒有的異常則會呼叫UNEXPECTED函式.我們可以通過set_unexpected()
原型是這樣大概 unexpexted_handlerset_unexpected
(unexpected_handler)
typedef void (*
unexpected_handler
)(); 和NEW_HANDLER一樣是個函式指標.
在函式中如果有類似程式碼throw;就會丟擲一個型別為bad_exception的異常.你當然也可以自己決定丟擲的型別,這樣就明確的知道哪個型別是發生了異常說明中沒有的異常.在CATCH中對它進行捕獲,就會避免預設的UNEXPECTED函式的行為 即終止程式!