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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C++异常处理机制详解

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


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

?

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

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

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

throw popOnEmpty();

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

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

int mathFunc( int i )

{

??? if ( i == 0 )

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

}

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

?

2. catch子句的語法

一個catch 子句由三部分構成:

1)關鍵字catch

2)異常聲明,在括號中的單個類型或單個對象聲明被(稱作異常聲明,exception declaration)

3)復合語句中的一組語句。

// stackExcp.h

class popOnEmpty { };

class popOnFull { };

catch ( pushOnFull )

{

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

??? return errorCode88;

}

?

3. 異常聲明可以只是一個類型聲明而不是對象聲明嗎?

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

catch ( pushOnFull eObj )

{

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

}

?

4. 異常聲明中異常對象的拷貝過程?

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

例1:按值傳遞。當進入catch 子句時,如果異常聲明聲明了一個對象,則用該異常對象的拷貝初始化這個對象。例中對象eObj 是用異常對象的值來初始化的,會調(diào)用拷貝構造函數(shù)。

void calculate( int op ) {

try {

mathFunc( op );

}

catch (pushOnFull eObj ) {

// eObj 是被拋出的異常對象的拷貝

}

}

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

void calculate( int op ) {

try {

mathFunc( op );

}

catch (pushOnFull &eObj ) {

// eObj 引用了被拋出的異常對象

}

}

?

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

在查找用來處理被拋出異常的catch 子句時,因為異常而退出復合語句和函數(shù)定義,這個過程被稱作棧展開(stack unwinding)。隨著棧的展開,在退出的復合語句和函數(shù)定義中聲明的局部變量的生命期也結束了。C++保證,隨著棧的展開,盡管局部類對象的生命期是因為拋出異常而被結束,但是這些局部類對象的析構函數(shù)也會被調(diào)用。

?

6. 異常拋出沒有在try塊中或拋出的異常沒有對應的catch語句來捕捉,結果如何?

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

?

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

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

例子如下:

try

????? ??{

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

??????? }

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

??????? {

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

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

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

??????????? throw;? //重新拋出異常, 并由另一個catch 子句來處理

??????? }

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

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

?

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

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

作用在于:1. 可以釋放在前面獲得的資源(如動態(tài)內(nèi)存),因為異常退出,這些資源為釋放。2. 捕獲其余類型的未知異常。

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

catch ( pushOnFull ) {}

catch ( popOnEmpty ) { }

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

?

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

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

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

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

具體參見4,7,17。

?

10. 異常對象的生命周期?

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

銷毀:該異常的最后一個catch 子句退出時銷毀

注意:因為異常可能在catch子句中被重新拋出,所以在到達最后一個處理該異常的catch 子句之前,異常對象是不能被銷毀的。

?

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

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

try { throw "exception";}

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

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

同樣的問題存在于:

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

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

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

以上兩例在編譯時不警告,運行時不出錯,是存在隱患的。

?

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

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

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

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

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

extern void no_problem() throw();

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

extern void problem();

?

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

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

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

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

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

void no_problem() throw();

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

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

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

// ok: no_problem() 比 pf2 更嚴格

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

// 錯誤: doit()沒有 pf3 嚴格

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

注:在VC和gcc上測試失敗。

?

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

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

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ī)范沒有 base::f1() 的嚴格

double f1( double ) throw ( string );

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

int f2( int ) throw ( int );

// ok: 派生 f3() 更嚴格

string f3( ) throw ( int );

// ...

};

?

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

int convert( int parm ) throw(string)

{

if ( somethingRather )

// 程序錯誤:

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

throw "help!";

}

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

注意:

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

例如:

class popOnEmpty : public stackExcp { };

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

{

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

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

}

?

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

int main( ) {

try {

// 拋出pushOnFull異常

}

catch ( Excp ) {

// 處理 popOnEmpty 和 pushOnFull 異常

throw;

}

catch ( pushOnFull ) {

// 處理 pushOnFull 異常

}

}

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

?

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

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

// 定義了虛擬函數(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)用基類實例

}

}

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

輸出結果:

An exception has occurred

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

int main( ) {

try {

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

}

catch ( Excp &eObj ) {

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

}

}

輸出結果:

trying to push the value 879 on a full stack

?

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

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

int main()

try {

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

}

catch ( pushOnFull ) {

// ...

}

catch ( popOnEmpty ) {

// ...

}

?

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

如下例,普通的try塊

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

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 ( ...) {

// 特殊處理

// 不能捕獲來自成員初始化表的異常

}

}

改進后如下,使用函數(shù)try 塊是保證“在構造函數(shù)中捕獲所有在對象構造期間拋出的異常”的惟一解決方案。關鍵字try 應該被放在成員初始化表之前,try 塊的復合語句包圍了構造函數(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)在能夠捕獲來自 ServiceCharge() 的異常了

}

?

參考文獻:

C++ Primer第三版

?

總結

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

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