C與C++中的異常處理3 (轉)
C++異常處理的基本語法和語義:namespace prefix = o ns = "urn:schemas--com::office" />
這次,我來概述標準C++異常處理的基本語法和語義。順便,我會將它和前兩次提到的技術進行比較。(在本文及以後,我將標準C++異常處理簡稱為EH,將的方法稱為SEH。)
1.1 基本語法和語義
EH引入了3個新的C++語言關鍵字:
l catch
l throw
l try
異常透過如下語句觸發
throw [expression]
透過“異常規格申明”定義它將丟擲什麼異常:
throw([type-ID-list])
可選項type-ID-list包含一個或多個型別的名字,以逗號分隔。這些異常靠try塊中的異常處理函式進行捕獲。
try compound-statement handler-sequence
處理函式佇列包含一個或多個處理函式,形式如下:
catch ( exception-declaration ) compound-statement
處理函式的“異常申明”指明瞭這個函式將捕獲什麼型別的異常。
和SEH一樣,跟在try和catch後面的語句必須刮在{}內,而整個try塊組成一條完整的大語句。
例子:
void f() throw(int, some_class_type)
{
int i;
// ... generate an 'int' exception
throw i;
// ...
}
int main()
{
try
{
f();
}
catch(int e)
{
// ... handle 'int' exception ...
}
catch(some_class_type e)
{
// ... handle 'some_class_type' exception ...
}
// ... possibly other handlers ...
return 0;
}
異常規格申明是EH特有的,SEH和MFC都沒有類似的東西。一個空的異常規格申明表明函式不丟擲任何異常:
void f() throw()
{
// ... function throws no exceptions ...
}
如果函式沒有異常規格申明,它可以丟擲任何型別的異常:
void f()
{
// ... function can throw anything or nothing ...
}
當函式拋異常時,關鍵字throw通常後面帶一個被丟擲的:
throw i;
然而,throw也可以不帶物件:
catch(int e)
{
// ... handle 'int' exception ...
throw;
}
它的效果是再次丟擲當前正被捕獲的物件(int e)。因為空throw的作用是再次丟擲已存在的異常物件,所以它必須位於catch語句塊中。MFC也有再次丟擲異常的功能,SEH則沒有,它沒有將異常物件交給過處理函式,所以沒什麼可再次丟擲的。
就象函式原型中的引數申明一樣,異常申明也可以是無名的:
catch(char *)
{
// ... handle 'char *' exception ...
}
當這個處理函式捕獲一個char *型的異常物件時,它不能操作這個物件,因為這個物件沒有名字。
異常申明還可以是這樣的特殊形式:
catch(...)
{
// ... handle any type of exception ...
}
就象不定引數中的“...”一樣,異常申明中的“...”可以匹配任何異常的型別。
1.2 標準異常物件的型別
標準庫函式可能報告錯誤。在C標準庫中的報錯方式在前面說過了。在C++標準庫中,有些函式丟擲特定的異常,而另外一些根本不拋任何異常。
因為C++標準中沒有明確規定,所以C++的庫函式可以丟擲任何物件或不拋。但C++標準推薦執行庫的實現透過丟擲定義在
namespace std
{
class logic_error; // : public exception
class ain_error; // : public logic_error
class invalid_argument; // : public logic_error
class length_error; // : public logic_error
class out_of_range; // : public logic_error
class runtime_error; // : public exception
class range_error; // : public runtime_error
class overflow_error; // : public runtime_error
class underflow_error; // : public runtime_error
}
這些(異常)類只對C++標準庫有力。在你自己的程式碼中,你可以丟擲(和捕獲)任何你所象要的型別。
1.3 標準中的其它申明
標準庫頭
namespace std
{
//
// types
//
class bad_exception;
class exception;
typedef void (*tenate_handler)();
typedef void (*unexpected_handler)();
//
// functions
//
terminate_handler set_terminate(terminate_handler) throw();
unexpected_handler set_unexpected(unexpected_handler) throw();
void terminate();
void unexpected();
bool uncaught_exception();
}
提要:
l exception是所有標準庫丟擲的異常的基類。
l uncaught_exception()函式在有異常被丟擲卻沒有被捕獲時返回true,其它情況返回false。它類似於SEH的函式AbnormalTermination()。
l terminate()是EH的應急處理。它在異常處理體系陷入了不可恢復狀態時被,經常是因為試圖重入(在前一個異常正處理過程中又拋了一個異常)。
l unexpected()在函式丟擲一個它沒有在“異常規格申明”中申明的異常時被呼叫。這個預料外的異常可能在退棧過程中被替換為一個bad_excetion物件。
l 執行庫提供了預設terminate_handler()和unexpected_handler() 函式處理對應的情況。你可以透過set_terminate()和set_unexpected()函式替換庫的預設版本。
1.4 異常生命期
EH執行於異常生命期的五個階段:
l 或執行庫遇到一個錯誤狀況(階段1)並且丟擲一個異常(階段2)。
l 程式的執行停止於異常點,開始搜尋異常處理函式。搜尋沿呼叫棧向上搜尋(很象SEH終止異常時的行為)。
l 搜尋結束於找到了一個異常申明與異常物件的靜態型別相匹配(階段3)。於是進入相應的異常處理函式。
l 異常處理函式結束後,跳到此異常處理函式所在的try塊下面最近的一條語句開始(階段5)。這個行為意味著C++標準中異常總是終止。
這些步驟演示於這個簡單的例子中:
#include
static void f(int n)
{
if (n != 0) // Stage 1
throw 123; // Stage 2
}
extern int main()
{
try
{
f(1);
printf("resuming, should never appearn");
}
catch(int) // Stage 3
{
// Stage 4
printf("caught 'int' exceptionn");
}
catch(char *) // Stage 3
{
// Stage 4
printf("caught 'char *' exceptionn");
}
catch(...) // Stage 3
{
// Stage 4
printf("caught typeless exceptionn");
}
// Stage 5
printf("terminating, after 'try' blockn");
return 0;
}
/*
When run yields
caught 'int' exception
terminating, after 'try' block
*/
1.5 基本原理
C標準庫的異常體系處理C++語言時有如下難題:
l 解構函式被忽略。既然C標準庫異常體系是為C語言設計的,它們不知道C++的解構函式。尤其,abort()、exit()和longjmp()在退棧或程式終止時不呼叫區域性物件的解構函式。
l 繁瑣的。查詢全域性物件或函式返回值導致了程式碼混亂-你必須在所有可能發生異常的地方進行明確的異常情況檢測,即使是異常情況可能實際上從不發生。因為這種方法是如此繁瑣,程式設計師們可能會故意“忘了”檢測異常情況。
l 無彈性的。Longjmp()“丟擲”的只能是簡單的int型。errno和signal()/raise()只使用了很小的一個值域集合,解析度很低。Abort()和exit()總是終止程式。Assert()只工作在de版本中。
l 非固有的。所有的C標準庫異常體系都需要執行庫的支援,它不是語言核心支援的。
微軟特有的異常處理體系也不是沒有限制的:
l SEH異常處理函式不是直接捕獲一個異常物件,而是透過查詢一個(概念性的)類似errno的全域性值來判斷什麼異常發生了。
l SEH異常處理函式不能組合,給定try塊的唯有的一個處理函式必須在執行期識別和處理所有的異常事件。
l MFC異常處理函式只能捕獲CException及派生型別的指標。
l 透過包含定義了MFC異常處理函式的宏的標頭檔案,程式包含了數百個無關的宏和申明。
l MFC和SEH都是專屬於與Microsoft相容的開發環境和執行平臺的。
標準C++異常處理避免了這些短處:
l 析構。在拋異常而進行退棧時,區域性物件的解構函式被按正確的順序呼叫。
l 不引人注目的。異常的捕獲是暗地裡的和自動的。程式設計師無需因錯誤檢測而搞亂設計。
l 精確的。因為幾乎任何物件都可以被丟擲和捕獲,程式設計師可以控制異常的內容和含義。
l 可伸縮的。每個函式可以有多個try塊。每個try塊可以有單個或一組處理函式。每個處理函式可以捕獲單個型別,一組型別或所有型別的異常。
l 可預測的。函式可以指定它們將拋的異常型別,異常處理函式可以指定它們捕獲什麼型別的異常。如果程式違反了其申明,標準庫將按可預測的、定義的方式執行。
l 固有的。EH是C++語言的一部分。你可以定義、throw和catch異常而不需要包含任何庫。
l 標準的。EH在所有的標準C++的實現中都可用。
基於更完備的想法,C++標準委員會考慮過兩個EH的設計,在D&E的16章。(For a more complete rationale, including alternative EH designs consred by the C++ Standard's committee, check out Chapter 16 of the D&E.)
1.6 小結
下次,我將更深入挖掘EH的語言核心特性和EH的標準庫支援。我也將展示Microsoft Visual C++實現EH的內幕。我將開始標誌出EH的那些Visual C++只部分支援或完全不支援的特性,並且尋找繞過這些限制的方法。
在我相信設計EH的基本原理是健全的的同時,我也認為EH無意中包含了一些嚴重的後果。不用責備C++標準的制訂者的短視,我理解設計和實現有效的異常處理是多麼的難。當我們遭遇到這些無意中的後果時,我將展示它們對你程式碼的微妙影響,並且推薦一些技巧來減輕其影響。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-992208/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 【C++】 C++異常捕捉和處理C++
- C++異常處理機制C++
- Kotlin DSL C++專案引入OpenCV異常處理(轉)KotlinC++OpenCV
- C++整理19_異常處理C++
- C++錯誤和異常處理C++
- 【C++】 63_C語言異常處理C++C語言
- C++ 異常處理機制詳解:輕鬆掌握異常處理技巧C++
- (十五)C++學習 | 強制型別轉換 異常處理C++型別
- C#中的異常處理機制C#
- 關於C++ 的異常處理,解答在這來看看吧~C++
- Java 異常表與異常處理原理Java
- 異常處理機制(二)之異常處理與捕獲
- SpringBoot中異常處理Spring Boot
- C#自定義異常 統一異常處理C#
- C++異常C++
- 29.Spring Boot中異常處理與REST格式處理Spring BootREST
- 瞭解下C# 異常處理C#
- 詳解C#異常處理C#
- Ruby中的TypeError異常處理Error
- C介面與實現—C裡面的異常處理機制
- C#基礎之前處理器,異常處理C#
- 異常的處理
- 《C++ Primer》學習筆記(五):迴圈、分支、跳轉和異常處理語句C++筆記
- 異常-throws的方式處理異常
- 異常篇——異常處理
- [轉載] Java異常處理習題Java
- python異常處理中finally的作用Python
- gRPC 中的異常該如何處理?RPC
- Java 中的異常處理機制Java
- springboot專案中的異常處理Spring Boot
- SpringBoot中的全域性異常處理Spring Boot
- spring中的統一異常處理Spring
- 異常處理
- 異常處理與推導式
- C++語言程式設計筆記 - 第12章 - 異常處理C++程式設計筆記
- 在 C++ 中捕獲 Python 異常C++Python
- JSP 異常處理如何處理?JS
- C++中的字串編碼處理C++字串編碼
- Java中的異常處理最佳實踐Java