C++及Windows異常處理(try,catch; __try,__finally; __try, __except)——一道筆試題引起的探究
int* p = 0x00000000; // pointer to NULL
puts( "hello ");
__try{
puts( "in try ");
__try{
puts( "in try ");
*p = 13; // causes an access violation exception;
}__finally{
puts( "in finally ");
}
}__except(puts( "in filter "), 1){
puts( "in except ");
}
puts( "world ");
/*
hello
in try
in try
in filter
in finally
in except
world
*/
上面的題目,我把答案列了出來。
常用C++的朋友,應該沒見過__try這種形式的語句,下面我把try,catch; __try,__finally; __try, __except這三對異常處理使用標示逐一說明- C++ 異常處理:try,catch
try
{
// 可能出錯的語句
// 如果有錯,就——
throw ... // 初始化一個異常物件(exception object)
}
catch( 型別名 [形參名] ) /* 異常說明符(exception specifier)*/
{
}
catch( 型別名 [形參名] )
{
}
C++的異常處理很簡單,就是如上的三個關鍵字,注意C++中throw,catch之後沒有Java等語言中的finally。
Q: 為何C++不提供“finally”結構?
A: 因為C++提供了另一種機制,完全可以取代finally,而且這種機制幾乎總要比finally工作得更好:就是——“分配資源即初始化”。(見《The C++ Programming Language》14.4節)基本的想法是,用一個區域性物件來封裝一個資源,這樣一來區域性物件的解構函式就可以自動釋放資源。這樣,程式設計師就不會“忘記釋放資源”了。 [譯註:因為C++的物件“生命週期”機制替他記住了 :O) ] 下面是一個例子:
class File_handle {
FILE* p;
public:
File_handle(const char* n, const char* a)
{ p = fopen(n,a); if (p==0) throw Open_error(errno); }
File_handle(FILE* pp)
{ p = pp; if (p==0) throw Open_error(errno); }
~File_handle() { fclose(p); }
operator FILE*() { return p; }
// ...
};
void f(const char* fn)
{
File_handle f(fn,"rw"); // open fn for reading and writing
// use file through f
}
在一個系統中,每一樣資源都需要一個“資源局柄”物件,但我們不必為每一個資源都寫一個“finally”語句。在實作的系統中,資源的獲取和釋放的次數遠遠多於資源的種類,所以“資源分配即初始化”機制產生的程式碼要比“finally”機制少。
好了,接下來,看另外兩組異常模型機制,它們是Windows系列作業系統平臺上提供的SEH模型,也就是說在C++中呼叫的時候,其實是呼叫Windows的API
SEH,又稱結構化異常處理.是設計Windows作業系統時提出一個種處理異常的方法。
- __try, __except
這組異常處理機制和C++的很相像,只是關鍵字是except而不是catch
catch 和 except 的一點不同: catch關鍵字後面往往好像接受一個函式引數一樣,可以是各種型別的異常資料物件;但是__except關鍵字則不同,它後面跟的卻是一個表示式(可以是各種型別的表示式)
下面是一個例子:
void main()
{
puts("hello");
// 定義受監控的程式碼模組
__try
{
puts("in try");
}
//定義異常處理模組
__except(1)
{
puts("in except");
}
puts("world");
}
1. 受監控的程式碼模組被執行(也即__try定義的模組程式碼);
2. 如果上面的程式碼執行過程中,沒有出現異常的話,那麼控制流將轉入到__except子句之後的程式碼模組中;
3. 否則,如果出現異常的話,那麼控制流將進入到__except後面的表示式中,也即首先計算這個表示式的值,之後再根據這個值,來決定做出相應的處理。
EXCEPTION_CONTINUE_EXECUTION (–1) 異常被忽略,控制流將在異常出現的點之後,繼續恢復執行。
EXCEPTION_CONTINUE_SEARCH (0) 異常不被識別,也即當前的這個__except模組不是這個異常錯誤所對應的正確的異常處理模組。系統將繼續到上一層的try-except域中繼續查詢一個恰當的__except模組。
EXCEPTION_EXECUTE_HANDLER (1) 異常已經被識別,也即當前的這個異常錯誤,系統已經找到了並能夠確認,這個__except模組就是正確的異常處理模組。控制流將進入到__except模組中。
小結:
(1) C++異常模型用try-catch語法定義,而SEH異常模型則用try-except語法;
(2) 與C++異常模型相似,try-except也支援多層的try-except巢狀。
(3) 與C++異常模型不同的是,try-except模型中,一個try塊只能是有一個except塊;而C++異常模型中,一個try塊可以有多個catch塊。
(4) 與C++異常模型相似,try-except模型中,查詢搜尋異常模組的規則也是逐級向上進行的。但是稍有區別的是,C++異常模型是按照異常物件的型別來進行匹配查詢的;而try-except模型則不同,它通過一個表示式的值來進行判斷。如果表示式的值為1(EXCEPTION_EXECUTE_HANDLER),表示找到了異常處理模組;如果值為0(EXCEPTION_CONTINUE_SEARCH),表示繼續向上一層的try-except域中繼續查詢其它可能匹配的異常處理模組;如果值為-1(EXCEPTION_CONTINUE_EXECUTION),表示忽略這個異常,注意這個值一般很少用,因為它很容易導致程式難以預測的結果,例如,死迴圈,甚至導致程式的崩潰等。
(5) __except關鍵字後面跟的表示式,它可以是各種型別的表示式,例如,它可以是一個函式呼叫,或是一個條件表示式,或是一個逗號表示式,或乾脆就是一個整型常量等等。最常用的是一個函式表示式,並且通過利用GetExceptionCode()或GetExceptionInformation ()函式來獲取當前的異常錯誤資訊,便於程式設計師有效控制異常錯誤的分類處理。
(6) SEH異常處理模型中,異常被劃分為兩大類:系統異常和軟體異常。其中軟體異常通過RaiseException()函式丟擲。RaiseException()函式的作用類似於C++異常模型中的throw語句。
- __try, __finally
try-finally語句的語法與try-except很類似,稍有不同的是,__finally後面沒有一個表示式,這是因為try- finally語句的作用不是用於異常處理,所以它不需要一個表示式來判斷當前異常錯誤的種類。另外,與try-except語句類似,try- finally也可以是多層巢狀的,並且一個函式內可以有多個try-finally語句,不管它是巢狀的,或是平行的。當然,try-finally多層巢狀也可以是跨函式的。
最關鍵的一點: “不管在何種情況下,在離開當前的作用域時,finally塊區域內的程式碼都將會被執行到”
void tmain()
{
puts("hello");
__try
{
puts("__try塊中");
// 注意,下面return語句直接讓函式返回了
return;
}
__finally
{
puts("__finally塊中");
}
puts("world");
}
上面的程式執行結果如下:
hello
__try塊中
__finally塊中
Press any key to continue
小結:
__finally塊被執行的流程時,無外乎三種情況。
第一種就是順序執行到__finally塊區域內的程式碼,這種情況很簡單,容易理解;
第二種就是goto語句或return語句引發的程式控制流離開當前__try塊作用域時,系統自動完成對__finally塊程式碼的呼叫;
第三種就是由於在__try塊中出現異常時,導致程式控制流離開當前__try塊作用域,這種情況下也是由系統自動完成對__finally塊的呼叫。
無論是第 2種,還是第3種情況,毫無疑問,它們都會引起很大的系統開銷,編譯器在編譯此類程式程式碼時,它會為這兩種情況準備很多的額外程式碼。
一般第2種情況,被稱為“區域性展開(LocalUnwinding)”;第3種情況,被稱為“全域性展開(GlobalUnwinding)”。在後面闡述SEH實現的時候會詳細分析到這一點。
第3種情況,也即由於出現異常而導致的“全域性展開”,對於程式設計師而言,這也許是無法避免的,因為你在利用異常處理機制提高程式可靠健壯性的同時,不可避免的會引起效能上其它的一些開銷。呵呵!這世界其實也算瞞公平的,有得必有失。
相關文章
- C++異常處理:try,catch,throw,finally的用法C++
- c# 異常處理try catch finally_throwC#
- Java之異常處理try{}catch(){}Java
- c#之異常處理tcbs_try_catch_finallyC#
- JAVA的異常處理機制(一)——try...catch...finallyJava
- [CareerCup] 14.2 Try-catch-finally Java中的異常處理Java
- 異常-try...catch的方式處理異常1
- 異常-try...catch的方式處理異常2
- Java try catch finallyJava
- Python異常處理 try、except和else的使用Python
- 在 SQL Server 中使用 Try Catch 處理異常SQLServer
- pypthon3精要(11)-try,except,else異常處理
- 【轉】java中異常與try catch finally詳解Java
- c++ try catch 問題C++
- 無需寫try/catch,也能正常處理異常
- PHP 的異常處理之try和catch用法小結PHP
- python try異常處理Python
- JavaScript try catch finally 語句JavaScript
- Java try catch finally 總結Java
- JavaScript try/catch/finally 語句JavaScript
- JS 使用try catch捕獲異常JS
- python中try except處理程式異常的三種常用方法Python
- Swift4 異常處理Try_Catch的使用之初見Swift
- Laravel try catchLaravel
- js try catchJS
- iOS的@try、@catch()iOS
- Nodejs try catch捕捉異常失效場景NodeJS
- SQL SERVER 裡的錯誤處理(try catch)SQLServer
- 前端魔法堂——異常不僅僅是try/catch前端
- Java中的try、catch、finally塊簡單的解析Java
- Will it finally: 關於 try/catch 的一些細節
- Java含有return 的try catch finally的執行順序Java
- try-except語句與else子句聯合使用處理可能出現的程式異常
- c# throw及try_catch關聯測試C#
- try-catch-finally,被你忽略掉的執行順序
- Java趣味分享:try/finallyJava
- js中try和catch的用法JS
- java中try catch塊的使用Java