C++ 異常處理

注意:本文中代碼均使用 Qt 開發(fā)編譯環(huán)境

異常處理###

1.throw表達(dá)式語法:
throw 表達(dá)式

2.try語句塊語法:
try
復(fù)合語句 // 代碼保護(hù)段
catch(異常接口聲明) // 異常的類型,與函數(shù)形參類似,可以是某個類型的值,也可以是引用
復(fù)合語句 // 異常處理程序
catch(異常接口聲明)
復(fù)合語句

如果某段程序中發(fā)現(xiàn)了自己不能處理的異常,就可以使用throw表達(dá)式拋擲這個異常,將它拋擲給調(diào)用者。throw的操作數(shù)表示異常類型,語法上與return語句的操作數(shù)相似,如果程序中有多處需要拋擲的異常,應(yīng)該用不同的操作數(shù)類型來相互區(qū)別,操作數(shù)的值不能用來區(qū)別不同的異常。

類型可以是任何有效的數(shù)據(jù)類型,包括C++的類。當(dāng)異常被拋擲后,catch子句便依次被檢查,若某個catch子句的異常類型聲明與被拋擲的異常類型一致,則執(zhí)行該段異常處理程序。如果異常類型聲明是一個省略號(…),catch子句便處理任何類型的異常,這段處理程序必須是try塊的最后一段處理程序。

例子:

#include <QCoreApplication>
#include <QDebug>

int Div(int x,int y);

int main(){

    try{
       qDebug() << "5/2 = " << Div(5, 2);
       qDebug() << "8/0 = " << Div(8, 0);
       qDebug() << "7/1 = " << Div(7, 1);
    } catch(int) {
       qDebug() << "except of deviding zero.";
    }

    qDebug() << "that's ok.";
    return 0;
}

int Div(int x, int y) {
    if(y==0) {
       throw y;
    }
    return x/y;
}

運行結(jié)果:

拋出異常

異常被拋擲后,程序流跳至try語句塊之外繼續(xù)執(zhí)行,例子中的下列語句沒有被執(zhí)行:

qDebug() << "7/1 = " << Div(7, 1);

另外,catch處理程序出現(xiàn)的順序很重要,因為在一個try塊中,異常處理程序是按照他出現(xiàn)的次序被檢查的。只要找到一個匹配的異常類型,后面的異常處理都將被忽略。

異常接口聲明###

為了加強程序的可讀性,使函數(shù)的用戶能夠方便的知道所使用的函數(shù)會拋出那些異常,可以在函數(shù)的聲明中列出這個函數(shù)可能拋擲的所有異常類型,例如:
void fun() throw (A,B,C,D);
這表明函數(shù)fun()能夠且只能夠拋擲類型A、B、C、D及其子類型的異常。

void fun();可以拋擲任何類型異常。

一個不拋擲任何類型異常的函數(shù)可以進(jìn)行如下形式的聲明:
void fun() throw();

異常處理中的構(gòu)造與析構(gòu)###

C++異常處理的真正能力,不僅在于它能夠處理各種不同類型的異常,還在于它具有為異常拋擲前構(gòu)造的所有局部對象自動調(diào)用析構(gòu)函數(shù)的能力。

在程序中,找到一個匹配的catch異常處理之后,如果catch字句的異常類型聲明是一個值參數(shù),則其初始化方式是復(fù)制被拋擲的異常對象。如果catch字句的異常類型聲明是一個引用,則其初始化方式是使該引用指向異常對象。

當(dāng)catch字句的異常類型聲明參數(shù)被初始化后,棧的展開過程便開始了。這包括將從對應(yīng)的try塊開始到異常被拋擲處之間構(gòu)造(且尚未構(gòu)造)的所有自動對象進(jìn)行析構(gòu)。析構(gòu)的順序與構(gòu)造的順序相反。然后程序從最后一個catch處理之后開始恢復(fù)執(zhí)行。

示例代碼:

#include <QCoreApplication>
#include <QDebug>

void MyFunc(void);

class Expt
{
public:
   Expt(){}
   ~Expt(){}

   const char *ShowReason() const{
       return "Expt 類異常。";
   }
};


class Demo{
public:
   Demo();
   ~Demo();
};

Demo::Demo(){
   qDebug()<<"構(gòu)造Demo。";
}

Demo::~Demo(){
   qDebug()<<"析構(gòu)Demo。";
}

void MyFunc(){
   Demo D;
   qDebug()<<"在MyFunc()中拋擲Expt類異常。";
   throw Expt();
}

int main()
{

   qDebug()<<"在main()函數(shù)中。";

   try {
       qDebug()<<"在try塊中,調(diào)用MyFunc()。";
       MyFunc();
   } catch (Expt E) {
       qDebug()<<"在catch異常處理程序中。";
       qDebug()<<"捕獲到Expt類型異常:";
       qDebug()<<"E.ShowReason(): " << E.ShowReason();
   } catch (char * str) {
       qDebug()<<"捕獲到其它的異常:"<<str;
   }

   qDebug() << "回到main函數(shù)。從這里恢復(fù)執(zhí)行。";

   return 0;
}

運行結(jié)果:

運行效果

注意在此例中,在兩個catch處理器中都說明了異常參量:

catch (Expt E){//…
} catch (char * str){//…
}

其實也可以不說明這些參量(E和str)。在很多情況下只要通知處理程序有某個特定類型的異常已經(jīng)產(chǎn)生就足夠了。但是需要訪問異常對象時就要說明參量,否則將無法訪問catch處理程序子句中的那個對象。例如:

catch (Expt E) {
//在這里不能訪問Expt異常對象
}

用一個不帶操作數(shù)的throw表達(dá)式可以將當(dāng)前正在被處理的異常再次拋擲,這樣一個表達(dá)式只能出現(xiàn)在一個catch處理程序中或在catch處理程序內(nèi)部調(diào)用的函數(shù)中。再次拋擲的異常對象是源異常對象(不是拷貝)。例如:
try {
throw CSomeOtherException();
} catch (…) //處理所有異常
{
//對異常做出響應(yīng)(也許僅僅是部分的)
//…
throw; //將異常傳遞給某個其它處理器
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容