內容概覽
一圖解百問,但是有些地方我們需要特別指出:
- 類型在這里指通過typedef重定義的,例如函數類型、指針類型等。
exception_ptr
在標准中是未定義具體實現的,因此它可能是類也可能是類型。uncaught_exception()
和uncaught_exceptions()
是兩個不同的函數,分別 判斷是否有未處理異常、返回未處理異常的個數 。 其中,uncaught_exceptions()
函數僅在C++14開始出現。
仔細觀察上圖,你就能發現標准庫給我們提供了以下幾個方面的異常處理支持:
- 未捕獲異常處理
- 異常嵌套
- 異常重拋
未捕獲異常處理
其實未捕獲異常可以分為兩類,一類有關try-catch塊,另一類有關dynamic-exception-specification(動態異常指定)。
try-catch
當拋出的異常未被catch塊捕獲時,標准庫的terminate()
函數會被自動調用,默認情況下該函數調用abort()
函數非正常終止程序。 非正常終止是什么意思呢? 這里我們引用標准的一句話來解釋:
The program is terminated without destroying any object and without calling any of the functions passed to atexit or at_quick_exit.
大概意思是說,非正常終止不會析構任何對象,也不會調用任何通過atexit 或者 at_quick_exit注冊的處理函數。
很明白的,程序的資源釋放會成為一個嚴重的問題。 因此,標准庫提供了set_terminate()
、get_terminate()
來幫助用戶獲取和設置處理器做一些必要的清理工作 、讓用戶來決定是否終止程序。
dynamic-exception-specification(C++11中已廢棄)
在C++11之前,函數簽名中還可以指定拋出的異常類型(如果有):
void function() throw(int) {...}
如上,如果該function函數拋出了任何非int異常類型,unexpected()
函數也會被自動調用。 為此,C++標准甚至特地規定了一個bad_exception
異常類來表示這種情況。 我們把這類情況也稱為未捕獲,該函數默認調用terminate()函數,不再贅述。
但是,但是,該特性在實踐中被證明非常“雞肋”, 因此從C++11開始被標記為廢棄。
異常嵌套
為了支持異常嵌套,標准庫提供了三個積木: nested_exception異常類 、throw_with_nested函數 、rethrow_if_nested函數。
如何嵌套
標准定義了一個異常類nested_exception,這個類非常特殊,它沒有繼承自通用的異常基類exception。 標准指出,該類是為了繼承之用,以配合其它兩個函數實現嵌套異常機制。
如何構造嵌套
答:使用函數構造並拋出,以下是模版函數原型:
template <class T> [[noreturn]] void throw_with_nested(T&& t);
該函數將當前異常類(正在處理的異常類)與傳入類型構造成一個嵌套類型, 當前異常類為nested-exception,傳入類型為outer-exception。
如何解嵌套
答: 使用函數解嵌套並重拋,以下是函數原型:
template <class E> void rethrow_if_nested(const E& e);
如果傳入異常類型為嵌套異常,該函數會拋出被嵌套的異常。
個人感覺
看樣子,C++標准是打算提供一個方便的異常嵌套模型供開發者使用,但是給我的感覺確非常別扭。 從嵌套構造上來講,嵌套異常的構造方式非常模糊,需要結合當前上下文,傳入一個outer-exception類型也很不舒服;從解嵌套的方法上來講,拋出似乎是比較合理的獲得方式,但該函數名(rethrow_if_nested)不夠友好----拋出?是拋出該嵌套異常呢還是被嵌套異常?
異常重拋
重拋的支持非常簡單,調用函數:
void rethrow_exception(exception_ptr p);
但是注意,傳參是exception_ptr類型,你想要重拋異常的話還需要經過一次類型轉換(通過make_exception_ptr()函數)。 -……-怪不得C++總被人詬病別扭。。。