異常是OO語言處理錯誤的方式,在C++中,鼓勵使用異常。侯捷再書中談起異常,“十年前撰寫“未將異常考慮在內的”函數是為一種美好實踐,而今我們致力于寫出“異常安全碼”。”可見異常安全的重要。
說起異常安全,首先就要是異常的出現是為彌補C語言缺陷。再者,將介紹異常的概念,異常安全的條件。
C語言處理錯誤的缺陷
- 程序意外終止
? ? ? ????????? 比如:內存申請錯誤,越界,除0錯誤,會直接終止程序
- 錯誤碼難以解讀
? ? ????????? ? 在出錯后會返回一個數字(錯誤碼)。此時會包含倆層含義:是錯誤信息?是結果?
? ? ? ? ????????錯誤碼需要程序員查找相關庫信息
? ? ?出現錯誤直接終止程序是非常不允許的情況。
C++異常的引入
異常:當一個函數出現自己無法解決的錯誤時,可以拋出異常,讓函數的直接或間接調用者處理這個問題。
處理異常的三個關鍵字:
throw:當問題出現時,要拋出異常,通過throw拋出
catch:用于捕獲異常。可以有多個catch
try:try中的代碼將被激活特定的異常,try后跟著一個或多個catch塊。
try要和catch匹配使用。
catch塊中的內容不一定會被執行,只有當異常拋出且被捕獲時才會執行,否則不執行。
try {//保護塊}catch (ExceptionName e1){//}catch (ExceptionName e2){//}
異常的拋出與匹配規則
- 異常是有拋出對象引發的,該對象類型決定調用哪個塊的。比如:拋出int類型的異常,catch參數為int的來接收。實際上:拋出和捕獲類型不一定要相同,這里可以拋出派生對象。
- 被選中的處理代碼是調用鏈中與該對象類型匹配且離拋出異常位置最近的那一個。
- 異常拋出的對象后,會生成一個臨時拷貝,傳給catch
在實際運用中,拋異常拋出通常對象是一個類,包含錯誤信息和錯誤碼。
由于私有成員在內外拿不到,故通過函數調用返回錯誤信息和錯誤碼。
對于臨時拷貝的類型是const 故函數要添加const,才可調用。
- catch(...)可以捕獲任意類型的對象。
拋出的異常在沒有繼承情況下,要匹配相應的類型才能被捕獲,會在catch中一直匹配,直到catch(...)處理任意類型。但是無法得到拋出的異常對象。

異常的重新拋出


異常安全
具有異常安全的函數會
- 不泄露任何資源。例如上述代碼在拋出異常后,后續的delete不會被調用。
- 不允許數據敗壞。異常拋出后,異常被捕獲,導致棧幀的跳躍,關鍵信息沒有被執行。
解決資源泄露是比較輕松的
確保析構,智能指針。
這里我們專注解決數據敗壞的問題。
在構造函數中,最好不要拋異常,可能會導致沒有完全構造
析構過程最好不好拋異常
在lock和unlock拋異常會導致死鎖
異常安全函數有以下三個保證
- 基本承諾
????????如果異常被拋出,程序內任何事物都保持在有效狀態,沒有任何對象和數據結構被破壞,所有對象處于一種內部前后一致的狀態。
- 強烈保證
? ? ? ? 如果函數調用成功,就完全成功。如果函數失敗,程序就恢復到調用之前狀態。
- 不拋擲保證
? ? ? ? 承諾異常絕不拋出,因為它們總能夠完成它們原先承諾的功能。
在C++11中,如果一個函數明確的不拋異常的話,就用noexcept
thread() noexcept;
thread (thread&& x) noexcept;
異常優點
- 可以清晰展示錯誤信息
- 拋異常可以直接拿到錯誤信息,不需要重重返回。
- 第三方庫的異常安全很規范
- 部分函數更好檢查。如構造函數沒有返回值。
缺點
執行的跳躍,亂流。追蹤程序困難。
C++沒有垃圾回收機制,異常任意導致內存泄漏
標準庫的異常不完善。
異常是被鼓勵使用。時間不斷前進,我們與時俱進!
參考:
<<Effective c++>>