日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程语言 > c/c++ >内容正文

c/c++

C++异常处理机制详解

發(fā)布時(shí)間:2023/12/2 c/c++ 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++异常处理机制详解 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.


異常處理是一種允許兩個(gè)獨(dú)立開發(fā)的程序組件在程序執(zhí)行期間遇到程序不正常的情況(異常exception)時(shí)相互通信的機(jī)制。本文總結(jié)了19個(gè)C++異常處理中的常見問(wèn)題,基本涵蓋了一般C++程序開發(fā)所需的關(guān)于異常處理部分的細(xì)節(jié)。

?

1. throw可以拋出哪些種類的異常對(duì)象?如何捕獲?

1)異常對(duì)象通常是一個(gè)class對(duì)象, 通常用以下代碼拋出:

// 調(diào)用的類的構(gòu)造函數(shù)

throw popOnEmpty();

但是throw 表達(dá)式也可以拋出任何類型的對(duì)象, 例如(雖然很不常見)在下面的代碼例子中,函數(shù)mathFunc()拋出一個(gè)枚舉類型的異常對(duì)象

enum EHstate { noErr, zeroOp, negativeOp, severeError };

int mathFunc( int i )

{

??? if ( i == 0 )

??? throw zeroOp; // 枚舉類型的異常

}

2)拋出異常的語(yǔ)句或其調(diào)用函數(shù)要在try塊中才能被捕獲。

?

2. catch子句的語(yǔ)法

一個(gè)catch 子句由三部分構(gòu)成:

1)關(guān)鍵字catch

2)異常聲明,在括號(hào)中的單個(gè)類型或單個(gè)對(duì)象聲明被(稱作異常聲明,exception declaration)

3)復(fù)合語(yǔ)句中的一組語(yǔ)句。

// stackExcp.h

class popOnEmpty { };

class popOnFull { };

catch ( pushOnFull )

{

??? cerr << "trying to push a value on a full stack\n";

??? return errorCode88;

}

?

3. 異常聲明可以只是一個(gè)類型聲明而不是對(duì)象聲明嗎?

catch 子句的異常聲明可以是一個(gè)類型聲明或一個(gè)對(duì)象聲明。當(dāng)我們要獲得throw 表達(dá)式的值或者要操縱throw 表達(dá)式所創(chuàng)建的異常對(duì)象時(shí),我們應(yīng)該聲明一個(gè)對(duì)象。

catch ( pushOnFull eObj )

{

??? cerr << "trying to push the value " << eObj.value() << " on a full stack\n";

}

?

4. 異常聲明中異常對(duì)象的拷貝過(guò)程?

catch 子句異常聲明的行為特別像參數(shù)聲明。同理,也可以分出按值傳遞和引用傳遞(指針)。通常采用的是引用傳遞。

例1:按值傳遞。當(dāng)進(jìn)入catch 子句時(shí),如果異常聲明聲明了一個(gè)對(duì)象,則用該異常對(duì)象的拷貝初始化這個(gè)對(duì)象。例中對(duì)象eObj 是用異常對(duì)象的值來(lái)初始化的,會(huì)調(diào)用拷貝構(gòu)造函數(shù)。

void calculate( int op ) {

try {

mathFunc( op );

}

catch (pushOnFull eObj ) {

// eObj 是被拋出的異常對(duì)象的拷貝

}

}

例2:引用傳遞。catch子句可以直接引用由throw 表達(dá)式創(chuàng)建的異常對(duì)象,而不是創(chuàng)建一個(gè)局部拷貝。可以防止不必要地拷貝大型類對(duì)象。

void calculate( int op ) {

try {

mathFunc( op );

}

catch (pushOnFull &eObj ) {

// eObj 引用了被拋出的異常對(duì)象

}

}

?

5. 異常處理的棧展開過(guò)程是什么?

在查找用來(lái)處理被拋出異常的catch 子句時(shí),因?yàn)楫惓6顺鰪?fù)合語(yǔ)句和函數(shù)定義,這個(gè)過(guò)程被稱作棧展開(stack unwinding)。隨著棧的展開,在退出的復(fù)合語(yǔ)句和函數(shù)定義中聲明的局部變量的生命期也結(jié)束了。C++保證,隨著棧的展開,盡管局部類對(duì)象的生命期是因?yàn)閽伋霎惓6唤Y(jié)束,但是這些局部類對(duì)象的析構(gòu)函數(shù)也會(huì)被調(diào)用。

?

6. 異常拋出沒(méi)有在try塊中或拋出的異常沒(méi)有對(duì)應(yīng)的catch語(yǔ)句來(lái)捕捉,結(jié)果如何?

異常不能夠保持在未被處理的狀態(tài),異常對(duì)于一個(gè)程序非常重要,它表示程序不能夠繼續(xù)正常執(zhí)行。如果沒(méi)有找到處理代碼,程序就調(diào)用C++標(biāo)準(zhǔn)庫(kù)中定義的函數(shù)terminate()。terminate()的缺省行為是調(diào)用abort() ,指示從程序非正常退出。

?

7.為什么要重新拋出異常?怎么寫?

在異常處理過(guò)程中也可能存在“單個(gè)catch 子句不能完全處理異常”的情況。在對(duì)異常對(duì)象進(jìn)行修改或增加某些信息之后,catch 子句可能決定該異常必須由函數(shù)調(diào)用鏈中更上級(jí)的函數(shù)來(lái)處理。表達(dá)式的形式為:throw;

例子如下:

try

????? ??{

??????????? entryDescr->checkMandatoryData(beModel_);

??????? }

??????? catch (CatchableOAMexception & error) // 只能用引用聲明

??????? {

??????????? vector<string> paramList;

??????????? paramList.push_back(currentDn);

??????????? error.addFrameToEnd(6,paramList);? // 修改異常對(duì)象

??????????? throw;? //重新拋出異常, 并由另一個(gè)catch 子句來(lái)處理

??????? }

注意1:被重新拋出的異常就是原來(lái)的異常對(duì)象,所以異常聲明一定要用引用。

注意2:在catch 語(yǔ)句里也可以拋出其它

?

8. 怎么捕捉全部異常或未知異常?

可以用catch ( ... ) { } 。

作用在于:1. 可以釋放在前面獲得的資源(如動(dòng)態(tài)內(nèi)存),因?yàn)楫惓M顺?#xff0c;這些資源為釋放。2. 捕獲其余類型的未知異常。

catch 子句被檢查的順序與它們?cè)趖ry 塊之后出現(xiàn)的順序相同。一旦找到了一個(gè)匹配,則后續(xù)的catch 子句將不再檢查。這意味著如果catch(...)與其他catch 子句聯(lián)合使用,它必須總是被放在異常處理代碼表的最后,否則就會(huì)產(chǎn)生一個(gè)編譯時(shí)刻錯(cuò)誤。例子如下:

catch ( pushOnFull ) {}

catch ( popOnEmpty ) { }

catch (...) { } // 必須是最后一個(gè)catch 子句

?

9. 為什么 catch 子句的異常聲明通常被聲明為引用?

1)可以避免由異常對(duì)象到 catch 子句中的對(duì)象的拷貝,特別是對(duì)象比較大時(shí)。

2)能確保catch子句對(duì)異常對(duì)象的修改能再次拋出。

3)確保能正確地調(diào)用與異常類型相關(guān)聯(lián)的虛擬函數(shù),避免對(duì)象切割。

具體參見4,7,17。

?

10. 異常對(duì)象的生命周期?

產(chǎn)生:throw className()時(shí)產(chǎn)生。

銷毀:該異常的最后一個(gè)catch 子句退出時(shí)銷毀

注意:因?yàn)楫惓?赡茉赾atch子句中被重新拋出,所以在到達(dá)最后一個(gè)處理該異常的catch 子句之前,異常對(duì)象是不能被銷毀的。

?

11. const char *到char * 非法的異常類型轉(zhuǎn)換。

我們注意到下面的代碼在VC中可以正常運(yùn)行(gcc不能)。

try { throw "exception";}

catch (char *) {cout << "exception catch!" <<endl;}

實(shí)際上throw的是一個(gè)const char *, catch的時(shí)候轉(zhuǎn)型成char *。這是C++對(duì)C的向下兼容。

同樣的問(wèn)題存在于:

1. char *p =? “test”; // 也是一個(gè)const char * 到char *轉(zhuǎn)型。

2. void func(char* p) { printf("%s\n", p); }

??? func("abc"); // const char * 到char *

以上兩例在編譯時(shí)不警告,運(yùn)行時(shí)不出錯(cuò),是存在隱患的。

?

12. 異常規(guī)范(exception specification)的概念?

異常規(guī)范在函數(shù)聲明是規(guī)定了函數(shù)可以拋出且只能拋出哪些異常。空的異常規(guī)范保證函數(shù)不會(huì)拋出任何異常。如果一個(gè)函數(shù)聲明沒(méi)有指定異常規(guī)范,則該函數(shù)可以拋出任何類型的異常。

例1:函數(shù)Pop若有異常,只能拋出popOnEmpty和string類型的異常對(duì)象

void pop( int &value ) throw(popOnEmpty, string);

例2:函數(shù)no_problem()保證不會(huì)拋出任何異常

extern void no_problem() throw();

例3:函數(shù)problem()可以拋出任何類型的異常

extern void problem();

?

13. 函數(shù)指針的異常規(guī)范?

我們也可以在函數(shù)指針的聲明處給出一個(gè)異常規(guī)范。例如:

void (*pf) (int) throw(string);

當(dāng)帶有異常規(guī)范的函數(shù)指針被初始化或被賦值時(shí),用作初始值或右值的指針異常規(guī)范必須與被初始化或賦值的指針異常規(guī)范一樣或更嚴(yán)格。例如:

void recoup( int, int ) throw(exceptionType);

void no_problem() throw();

void doit( int, int ) throw(string, exceptionType);

// ok: recoup() 與 pf1 的異常規(guī)范一樣嚴(yán)格

void (*pf1)( int, int ) throw(exceptionType) = &recoup;

// ok: no_problem() 比 pf2 更嚴(yán)格

void (*pf2)() throw(string) = &no_problem;

// 錯(cuò)誤: doit()沒(méi)有 pf3 嚴(yán)格

void (*pf3)( int, int ) throw(string) = &doit;

注:在VC和gcc上測(cè)試失敗。

?

14. 派生類中虛函數(shù)的異常規(guī)范的聲明?

基類中虛擬函數(shù)的異常規(guī)范,可以與派生類改寫的成員函數(shù)的異常規(guī)范不同。但是派生類虛擬函數(shù)的異常規(guī)范必須與基類虛擬函數(shù)的異常規(guī)范一樣或者更嚴(yán)格。

class Base {

public:

virtual double f1( double ) throw ();

virtual int f2( int ) throw ( int );

virtual string f3( ) throw ( int, string );

// ...

};

class Derived : public Base {

public:

// error: 異常規(guī)范沒(méi)有 base::f1() 的嚴(yán)格

double f1( double ) throw ( string );

// ok: 與 base::f2() 相同的異常規(guī)范

int f2( int ) throw ( int );

// ok: 派生 f3() 更嚴(yán)格

string f3( ) throw ( int );

// ...

};

?

15. 被拋出的異常的類型和異常規(guī)范中指定的類型能進(jìn)行類型轉(zhuǎn)換嗎?

int convert( int parm ) throw(string)

{

if ( somethingRather )

// 程序錯(cuò)誤:

// convert() 不允許 const char* 型的異常

throw "help!";

}

throw 表達(dá)式拋出一個(gè)C 風(fēng)格的字符串,由這個(gè)throw 表達(dá)式創(chuàng)建的異常對(duì)象的類型為const char*。通常,const char*型的表達(dá)式可以被轉(zhuǎn)換成string 類型。但是,異常規(guī)范不允許從被拋出的異常類型到異常規(guī)范指定的類型之問(wèn)的轉(zhuǎn)換。

注意:

當(dāng)異常規(guī)范指定一個(gè)類類型(類類型的指針)時(shí),如果一個(gè)異常規(guī)范指定了一個(gè)類,則該函數(shù)可以拋出“從該類公有派生的類類型”的異常對(duì)象。類指針同理。

例如:

class popOnEmpty : public stackExcp { };

void stackManip() throw( stackExcp )? // 異常規(guī)范是stackExcp類型

{

??? throw stackExcp();??????????? // 與異常規(guī)范一樣

??? throw popOnEmpty ();????? // ok. 是stackExcp的派生類

}

?

16. 公有基類的catch子句可以捕捉到其派生類的異常對(duì)象。

int main( ) {

try {

// 拋出pushOnFull異常

}

catch ( Excp ) {

// 處理 popOnEmpty 和 pushOnFull 異常

throw;

}

catch ( pushOnFull ) {

// 處理 pushOnFull 異常

}

}

在上例中,進(jìn)入catch ( Excp )子句,重新拋出的異常任然是pushOnFull類型的異常對(duì)象,而不會(huì)是其基類對(duì)象Excp。

?

17. 異常對(duì)象中怎么運(yùn)用虛擬函數(shù)來(lái)完成多態(tài)?

1)異常申明是對(duì)象(不是引用或指針),類似于普通的函數(shù)調(diào)用,發(fā)生對(duì)象切割。

// 定義了虛擬函數(shù)的新類定義

class Excp {

public:

virtual void print() {

cerr << "An exception has occurred"

<< endl;

}

};

class stackExcp : public Excp { };

class pushOnFull : public stackExcp {

public:

virtual void print() {

cerr << "trying to push the value " << _value

<< " on a full stack\n";

}

// ...

};

int main( ) {

try {

// iStack::push() throws a pushOnFull exception

} catch ( Excp eObj ) {

eobj.print(); // 調(diào)用虛擬函數(shù)

// 喔! 調(diào)用基類實(shí)例

}

}

對(duì)象切割過(guò)程:eObj 以“異常對(duì)象的基類子對(duì)象Excp 的一個(gè)拷貝”作為初始值,eobj 是Excp 類型的對(duì)象,而不是pushOnFull 類型的對(duì)象。

輸出結(jié)果:

An exception has occurred

2)異常聲明是一個(gè)指針或引用

int main( ) {

try {

// iStack::push() 拋出一個(gè) pushOnFull 異常

}

catch ( Excp &eObj ) {

eobj.print(); // 調(diào)用虛擬函數(shù) pushOnFull::print()

}

}

輸出結(jié)果:

trying to push the value 879 on a full stack

?

18. function try block(函數(shù)try塊)

把整個(gè)函數(shù)體包含在一個(gè)try塊中

int main()

try {

// main() 的函數(shù)體

}

catch ( pushOnFull ) {

// ...

}

catch ( popOnEmpty ) {

// ...

}

?

19. 為什么類的構(gòu)造函數(shù)需要函數(shù)try塊?

如下例,普通的try塊

inline Account::無(wú)法處理成員初始化表中的異常,若serviceCharge拋出異常,則這個(gè)異常無(wú)法被捕捉到。

Account( const char* name, double opening_bal )

: _balance( opening_bal - serviceCharge() )

{

try {

_name = new char[ strlen(name)+1 ];

strcpy( _name, name );

_acct_nmbr = get_unique_acct_nmbr();

}

catch ( ...) {

// 特殊處理

// 不能捕獲來(lái)自成員初始化表的異常

}

}

改進(jìn)后如下,使用函數(shù)try 塊是保證“在構(gòu)造函數(shù)中捕獲所有在對(duì)象構(gòu)造期間拋出的異常”的惟一解決方案。關(guān)鍵字try 應(yīng)該被放在成員初始化表之前,try 塊的復(fù)合語(yǔ)句包圍了構(gòu)造函數(shù)體。

inline Account::

Account( const char* name, double opening_bal )

try

: _balance( opening_bal - serviceCharge() )

{

_name = new char[ strlen(name)+1 ];

strcpy( _name, name );

_acct_nmbr = get_unique_acct_nmbr();

}

catch ( ... )

{

// 特殊處理

// 現(xiàn)在能夠捕獲來(lái)自 ServiceCharge() 的異常了

}

?

參考文獻(xiàn):

C++ Primer第三版

?

總結(jié)

以上是生活随笔為你收集整理的C++异常处理机制详解的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。