注意:本文中代碼均使用 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; //將異常傳遞給某個其它處理器
}