C++異常機制探究
異常是程式開發過程中的一個重要的內容,一旦程式執行過程中發生異常,如果不進行處理,程式將往未知的方向發展,開發出來的軟體不為使用者接受,嚴重影響使用者體驗。為了加深自己對C++異常機制的理解,通過理論+實踐的方式對異常機制進行探究與實踐。
程式在執行過程中,因為種種原因,難免會發生錯誤,發生錯誤並不可怕,可怕的是發生錯誤了不能及時解決,最終導致程式異常終止,輕則挨批,重則被使用者投訴。
傳統的錯誤處理機制是通過函式返回值進行控制的。引入異常以後,可以更加高效的處理錯誤。
C++的異常處理機制使得異常的引發和異常的處理不必在同一個函式中,這樣底層的函式可以著重解決具體問題,而不必過多的考慮異常的處理。上層呼叫者可以再適當的位置設計對不同型別異常
異常是專門針對抽象程式設計中的一系列錯誤處理的,C++中不能借助函式機制,因為棧結構的本質是先進後出,依次訪問,無法進行跳躍,但錯誤處理的特徵卻是遇到錯誤資訊就想要轉到若干級之上進行重新嘗試。
異常超脫於函式機制,決定了其對函式的跨越式回跳。異常跨越函式。
C++異常基本語法:
try
{
//可能發生異常的程式碼段
}
catch(exception &e) //處理異常
{
}
catch(...) //處理其它異常
{
}
系統本身可以處理異常,一旦系統無法處理,並且程式沒有適當處理,則會呼叫terminate函式,最終的結果就是abort。程式異常終止了。
環境:win7 64bit VS2015。
自定義異常類:
MyException.h
#pragma once #include <iostream> #include <string> using std::string; using std::exception; using std::cout; using std::endl;
class MyException:public exception //繼承自std名字空間的exception類。對exception的虛擬函式進行重寫以便輸出異常結果。 { public: MyException(string s=""); const char* what() const throw(); //對exception的what成員函式進行重寫。其原型保持同exception一致。throw()為不拋異常,C++11中throw()改成了nonexcept. private: string m_string; };
MyException::MyException(string s) { m_string = s; }
const char* MyException::what() const throw() { return m_string.c_str(); }
設計好自定義異常類以後,就可以通過throw MyException()的方式丟擲異常了。
捕捉異常:
void e1test(int i, int len) //用於測試越界異常 { if (i >= len) throw MyException("out of range!"); }
int divide(int x, int y) //用於測試除零異常 { if (y == 0) { char buf[100] = { 0 }; sprintf(buf, "%d is divided by zero!", x); throw MyException(buf); } return x / y; } void MyExceptionTest() //異常測試主函式 { vector<int> myvector; int i = 0; try { //cout<<divide(10, 0)<<endl; //此處會發生異常,丟擲一個MyException物件,字串的值為 “10 is divided by zero!” //myvector.at(i) = i; //vector的at方法會檢查越界,如果越界則丟擲異常out_of_range,系統能識別它,也可以使用者自定義 //assert(i < myvector.size());//匹配的處理器未找到,執行函式terminate被自動呼叫,其預設功能是呼叫abort終止程式。
e1test(i, myvector.size());//丟擲越界異常,使用者自定義處理 myvector[i] = i; //若不做異常處理,則發生錯誤,Debug Assertion Failed! } /*catch (out_of_range) //out_of_range是一個類 { cout << "out_of_range" << endl; }*/ catch (exception& e) //此處會多型,具體呼叫那種異常要看異常的動態型別。 { cout << e.what() << endl; } catch (...) //其他異常,例如丟擲一個系統檢測不到的異常,如 throw "123"; { cout << "other error" << endl; } }
異常被丟擲後,從進入try塊起,到異常被拋擲前,這期間在棧上的構造的所有物件,都會被自動析構。析構的順序與構造的順序相反。這一過程稱為棧的解旋(unwinding)。
異常的介面宣告:
為了加強程式的可讀性,可以在函式宣告中列出可能丟擲的所有異常型別,例如
void func() throw (e1, e2, e3); //這個函式func()能夠且只能丟擲型別e1 e2 e3及其子型別的異常。
如果在函式宣告中沒有包含異常介面宣告,則次函式可以拋擲任何型別的異常,例如
void func();
一個不拋擲任何型別異常的函式可以宣告為:
void func() throw(); 例如cosnt char *what()const throw();
如果一個函式丟擲了它的異常介面宣告所不允許丟擲的異常,unexpected函式會被呼叫,該函式預設行為呼叫terminate函式中止程式。