|
Posted on 2009-05-19 11:33 Prayer 閱讀(486) 評論(0) 編輯 收藏 引用 所屬分類: C/C++ 、 LINUX/UNIX/AIX
C++語言提供對處理異常情況的內部支持,異常情況即是所知道的“異常”,可能在你的程序執行期間出現。 try、throw和catch語句已被加到C++語言中去實現異常處理。有了C++異常處理,你的程序可以向更高的執行上下文傳遞意想不到的事件,這些上下文能更好地從這些異常事件中恢復過來。這些異常由正常控制流外的代碼進行處理。Microsoft C++編譯器朝著C++進化中的標準去實現基于ISO WG21/ANSI X3J16工作文件的C++異常處理模式。 語法 try塊: try復合語句 處理器表 處理器表: 處理器 處理器表opt 處理器: catch(異常說明) 復合語句 異常說明: 類型指示符表 說明符 類型指示符表 抽象說明符 類型指示符表 ...throw-表達式: throw 賦值表達式opt try子句后的復合語句是代碼的保護段。throw表達式“丟棄”(凸起)一個異常,catch子句后的復合語句是異常處理器,“捕獲”(處理)由throw表達式丟棄的異常。異常說明語句指示子句處理的異常的類型,類型可以是任何有效的數據類型,包括C++的類。如果異常說明語句是一個省略號(...),catch子句處理任何類型的異常,包括C的異常。這樣的處理器必須是其try塊的最后一個處理器。 throw的操作數語法上與return語句的操作數相似。注意:Microsoft C++不支持函數throw特征機制,如ANSI C++草案的15.5節所描述的。此外,它也不支持ANSI C++草案的15節中描述的function-try-block。執行過程如下: 1. 控制通過正常的順序執行到達try語句,保護段(在try塊內)被執行。 2. 如果在保護段執行期間沒有引起異常,跟在try塊后的catch子句不執行。從異常被丟棄的try塊后跟隨的最后一個catch子句后面的語句繼續執行下去。 3. 如果在保護段執行期間或在保護段調用的任何例行程序中(直接或間接的調用)有異常被丟棄,則從通過throw操作數創建的對象中創建一個異常對象(這隱含指可能包含一個拷貝構造函數)。在此點,編譯器在能夠處理丟棄類型的異常的更高執行上下文中尋找一個catch子句(或一個能處理任何類型異常的catch處理器)。catch處理程序按其在try塊后出現的順序被檢查。如果沒有找到合適的處理器,則下一個動態封閉的try塊被檢查。此處理繼續下去直到最外層封閉try塊被檢查完。 4. 如果匹配的處理器未找到,或如果在不自動分行時出現異常,但在處理器得到控制之前預定義的運行函數terminate被調用。如果一個異常發生在丟棄異常之后,則在循環展開開始之前調用terminate。 5. 如果一個匹配的catch處理器被找到,且它通過值進行捕獲,則其形參通過拷貝異常對象進行初始化。如果它通過引用進行捕獲,則參量被初始化為指向異常對象,在形參被初始化之后,“循環展開棧”的過程開始。這包括對那些在與catch處理器相對應的try塊開始和異常丟棄地點之間創建的(但尚未析構的)所有自動對象的析構。析構以與構造相反的順序進行。catch處理器被執行,且程序恢復到跟隨在最后的處理器之后的執行(即不是catch處理器的第一條語句或構造)。控制僅能通過一個丟棄的異常輸入一個catch處理器,而永遠不能通過goto語句或switch語句中的case標號。 以下是一個try塊和其相應的catch處理器的簡單例子,此例子檢測使用new運算符的存儲器分配操作的失敗。如果new成功了,則catch處理器永不執行: #include <iostream.h> int main() { char *buf; try { buf=new char[512]; if(buf==0) throw "Memory allocation failure!"; } catch (char *str) { cout << "Exception raised: " << str <<′\n′; } //... return 0; } throw表達式的操作數指示一個char*類型的異常正被丟棄。它由表示有捕獲char*類型的一個異常的能力的catch處理器進行處理。在存儲器分配失敗事件中,這是從前面例子得到的輸出: Exception raised: Memory allocation failure! C++異常處理的真正能力不僅在于其處理各種不同類型的異常的能力,還在于在堆棧循環展開期間為異常丟棄前構造的所有局部對象自動調用析構函數的能力。 以下例子演示了使用帶析構語義的類的C++異常處理: #include <iostream.h> void MyFunc(void); class CTest { public: CTest() {}; ~CTest() {}; const char *ShowReason() const { return "Exception in CTest class.";} }; class CDtorDemo { public: CDtorDemo(); ~CDtorDemo(); }; CDtorDemo::CDtorDemo() { cout << "Constructing CDtorDemo.\n"; } CDtorDemo::~CDtorDemo() { cout << "Destructing CDtorDemo.\n"; } void MyFunc() { CDtorDemo D; cout << "In MyFunc(). Throwing CTest exception.\n"; throw CTest(); } int main() { cout << "In main.\n"; try { cout << "In try block, calling MyFunc().\n"; MyFunc(); } catch (CTest E) { cout << "In catch handler.\n"; cout << "Caught CTest exception type:"; cout << E.ShowReason() << "\n"; } catch (char *str) { cout << "Canght some other exception:" << str << "\n"; } cout << "Back in main. Execution resumes here.\n"; return 0; } 以下是上面例子的輸出: In main.In try block, calling MyFunc() .Constructing CDtorDemo. In MyFunc(). Throwing CTest exception. Destructing CDtorDemo. In catch handler. Caught CTest exception type; Exception in CTest class. Back in main. Execution resumes here. 注意在此例中,異常參量(catch子句的參量)在兩個catch處理器中都被說明: catch(CTest E) //... catch(char *str) //... 你不需要說明此參量;在很多情況下可能通知處理器有某個特定類型的異常已經產生就足夠了。但是如果你在異常說明中沒有說明一個異常對象,你將無法訪問catch處理程序子句中的那個對象。 一個不帶操作數的throw表達式把當前正被處理的異常再次丟棄,這樣一個表達式僅僅應該出現在一個catch處理器中或從catch處理器內部被調用的函數中,再次丟棄的異常對象是源異常對象(不是拷貝)。例如: try { throw CSomeOtherException(); } catch(...) //處理所有異常 { //對異常作出響應(也許僅僅是部分的) //... throw; //將異常傳給某個其它處理器 } /*********************************************************/ setjmp和longjmp #include int setjmp(jmp_buf envbuf) 宏函數setjmp()在緩沖區envbuf中保存系統堆棧里的內容,供longjmp()以后使用,setjmp()必須使用頭文件setjmp.h。 調用setjmp()宏時,返回值為0,然而longjmp()把一個變原傳遞給setjmp(),該值(恒不為0)就是調用longjmp()后出現的setjmp()的值。 #include void longjmp(jmp_buf envbuf,int status); 函數longjmp()使程序在最近一次調用setjmp()出重新執行。setjmp()和longjmp()提供了一種在函數間調轉的手段,必須使用頭部文件setjmp.h。 函數longjmp()通過把堆棧復位成envbuf中描述的狀態進行操作,envbuf的設置是由預先調用setjmp()生成的。這樣使程序的執行在setjmp()調用后的下一個語句從新開始,使計算機認為從未離開調用setjmp()的函數。從效果上看,longjmp()函數似乎“繞”過了時間和空間(內存)回到程序的原點,不必執行正常的函數返回過程。 緩沖區envbuf具有中定義的buf_jmp類型,它必須調用longjmp()前通過調用setjmp()來設置好。 值status變成setjmp()的返回值,由此確定長調轉的來處。不允許的唯一值是0,0是程序直接調用函數setjmp()時由該函數返回的,不是間接通過執行函數longjmp()返回的。 longjmp()函數最常用于在一個錯誤發生時,從一組深層嵌套的實用程序中返回。 例子 這個例子輸出“1 2 3“ #include jmp_buf ebuf; void f2(void); int main(void) { int i; printf("1"); i=setjmp(ebuf); if(i==0) { f2(); printf("This will not be printed."); } printf("%d",i); return 0; } void f2(void) { printf("2"); longjmp(ebuf,3); } c語言的異常處理 利用setjmp和longjmp處理異常
c語言種可以利用setjmp和longjmp來實現程序的直接跳轉,這種機制并不適用于在程序開發中設計邏輯流程,但用來實現異常處理機制則是非常的適用。
比如寫一個實現內存分配的函數,可以這樣:
void *allocate(unsigned n) { void *new=malloc();
if (new) return new;
if (Allocation_handled) //如果new為NULL,則執行到此判斷 longjmp(Allocate_Failed,1); //跳轉
assert(0); }
|
而在調用時,可以先設好跳轉的“目的地”,再調用allocate:
char *buf; Allocation_handled=1;
if ( setjmp(Allocate_Failed) ) { fprintf(stderr,"Couldn't allocate the buffer! "); exit(1); }
buf = allocate(1000000);
Allocation_handled=0
|
為了使用方便可以把以上setjmp和longjmp做一個宏定義,這里就不細說了。但總的來說這個異常處理機制是不如c++的那么方便,但對于c語言來說也算不錯了。
|