程序設計中關於異常機制的思考

    程序的運行過程從來都不是一帆風順的,運行期間會遇到各式各樣的突發狀況,如文件打不開、內存分配錯誤、數據庫連不上等等。作爲一個進階過程中的編程人員,思考和處理例外狀況極爲重要。因爲它在很大程度保證了程序的連貫性和穩定性,併爲問題的發現提供支撐。

    下面就本人在編程過程中有關異常的編程範式做一下總結。


一、面向過程形式


    面向過程式的範式將異常的傳遞都交於函數的參數與返回值來處理,如:

bool func ( const InType& input, OutType& output, string& errMsg );

在函數中func中,bool類型的返回值用於標明函數的運行結果是否有異常,input是函數輸入,output是函數的運行結果的引用,errMsg是異常信息的引用。output和errMsg都是用於向func函數的調用者傳遞信息。

    當函數存在異常時,異常的信息就被保存在errMsg中,此外errMsg中還可以加上出現此異常的位置信息,例如異常出現在哪個函數中;而沒有發生異常時,errMsg只需要單純的加上此時的位置信息即可。這樣,一旦發生異常,異常信息就會如同一個堆棧那樣被傳到最上層的調用者那裏。如:

//定義三個用於嵌套調用的函數
bool func1 ( const InType& input, OutType& output, string& errMsg )
{
    ...
    
    //此處出現異常
    if(hasException())
    {
        //異常處理
         handleException();
        errMsg += "func1:Error occurs->";
        return false;   
    }
    
    ...
}
bool func2 ( const InType& input, OutType& output, string& errMsg )
{  
    ...
    
    //此處調用func1
    if(!func1(input, output, errMsg))
    {
         //異常處理
         handleException();
         errMsg += "func2:Call func1 Error->"
         return false;   
    }
    
    ...
}
bool func3 ( const InType& input, OutType& output, string& errMsg )
{
    ...
    
    //此處調用func2
    if(!func2(input, output, errMsg))
    {
         //異常處理
         handleException();
         errMsg += "func3:Call func2 Error->"
         return false;   
    }
    
    ...
}


// 客戶端調用函數
void call()
{
    ...
    
    //此處調用func3
    if(!func3(input, output, errMsg))
    {
         //異常處理
         handleException();
         errMsg += "call:Call func3 Error"
         print errMsg;  
    }
    
    ...
}

此時,輸出的錯誤信息爲:

func1:Error occurs->func2:Call func1 Error->func3:Call func2 Error->call:Call func3 Error

這樣錯誤棧就這樣展示出來了。


二、面向對象形式


    面嚮對象語言一般都自帶有異常處理機制,通常爲Exception類。若出現異常,可以就近處理,也可以向上級調用函數拋出。

    筆者剛接觸面向對象時,曾經非常不理解其異常機制存在的理由,認爲它很雞肋。現在隨着對編程理解的深入,越來越覺得這種異常機制的強大之處,尤其是對大型系統而言。

    首先,異常被封裝成了對象,可以自己定義,不必像面向過程的形式那樣都表現爲字符串。

//異常類的定義
class Exception{
public:
    Exception(const string& errMsg):m_errMsg(errMsg){}
    
    string errMsg(){
        return m_errMsg;
    }
private:
    string m_errMsg;
};

class Exception1:public Exception{...}

class Exception2:public Exception{...}

class Exception3:public Exception{...}

    其次,一個函數可以拋出多個異常,同樣外層調用函數也可以同時捕捉多個異常。

//拋出異常的函數
OutType func(InType input)
{
        ...
    
    //此處出現異常
    if(hasException1())
    {
        //異常處理
        handleException1();
        string errMsg = "Exception1 occurs";
        throw Exception1(errMsg );   
    }
    
    ...
        if(hasException2())
    {
        //異常處理
        handleException2();
        string errMsg = "Exception2 occurs";
        throw Exception2(errMsg );   
    }
    
    ...
        if(hasException3())
    {
        //異常處理
        handleException3();
        string errMsg = "Exception3 occurs";
        throw Exception3(errMsg );   
    }
    
    ...
    return output;
}

// 外層調用函數
void call()
{
    ...
    try{
        //調用 func 函數
        OutType output = func(input);
        
    }catch(Exception1 e1){
        handleException(e1);
        print e1.errMsg();
    }catch(Exception1 e2){
        handleException(e2);
        print e2.errMsg();
    }catch(Exception1 e3){
        handleException(e3);
        print e3.errMsg();
    }
    ...
}

    最後,這種異常機制將函數的參數和返回值從異常的傳遞任務中解放了出來,函數不必再通過參數返回錯誤信息,返回值也可以返回這個函數真正的調用結果,而不用向面向過程的形式那樣返回是否有異常。


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章