c++异常Try catch
分類:?c++專區(qū)?功能函數(shù)測(cè)試集錦2014-04-09 17:43?1505人閱讀?評(píng)論(0)?收藏?舉報(bào)
一、簡(jiǎn)單的例子
首先通過(guò)一個(gè)簡(jiǎn)單的例子來(lái)熟悉C++ 的 try/catch/throw:
catch 的數(shù)據(jù)類型需要與throw出來(lái)的數(shù)據(jù)類型相匹配的。
?
二、catch(...)的作用
catch(…)能夠捕獲多種數(shù)據(jù)類型的異常對(duì)象,所以它提供給程序員一種對(duì)異常對(duì)象更好的控制手段,使開(kāi)發(fā)的軟件系統(tǒng)有很好的可靠性。因
此一個(gè)比較有經(jīng)驗(yàn)的程序員通常會(huì)這樣組織編寫它的代碼模塊,如下:
void Func()
{
try
{
??? // 這里的程序代碼完成真正復(fù)雜的計(jì)算工作,這些代碼在執(zhí)行過(guò)程中
??? // 有可能拋出DataType1、DataType2和DataType3類型的異常對(duì)象。
}
catch(DataType1& d1)
{
}
catch(DataType2& d2)
{
}
catch(DataType3& d3)
{
}
// 注意上面try block中可能拋出的DataType1、DataType2和DataType3三
// 種類型的異常對(duì)象在前面都已經(jīng)有對(duì)應(yīng)的catch block來(lái)處理。但為什么
// 還要在最后再定義一個(gè)catch(…) block呢?這就是為了有更好的安全性和
// 可靠性,避免上面的try block拋出了其它未考慮到的異常對(duì)象時(shí)導(dǎo)致的程
// 序出現(xiàn)意外崩潰的嚴(yán)重后果,而且這在用VC開(kāi)發(fā)的系統(tǒng)上更特別有效,因
// 為catch(…)能捕獲系統(tǒng)出現(xiàn)的異常,而系統(tǒng)異常往往令程序員頭痛了,現(xiàn)
// 在系統(tǒng)一般都比較復(fù)雜,而且由很多人共同開(kāi)發(fā),一不小心就會(huì)導(dǎo)致一個(gè)
// 指針變量指向了其它非法區(qū)域,結(jié)果意外災(zāi)難不幸發(fā)生了。catch(…)為這種
// 潛在的隱患提供了一種有效的補(bǔ)救措施。
catch(…)
{
}
}
三、異常中采用面向?qū)ο蟮奶幚?br /> 首先看下面的例子:
void OpenFile(string f)
{
try
{
?? // 打開(kāi)文件的操作,可能拋出FileOpenException
}
catch(FileOpenException& fe)
{
?? // 處理這個(gè)異常,如果這個(gè)異常可以很好的得以恢復(fù),那么處理完畢后函數(shù)
?? // 正常返回;否則必須重新拋出這個(gè)異常,以供上層的調(diào)用函數(shù)來(lái)能再次處
?? // 理這個(gè)異常對(duì)象
?? int result = ReOpenFile(f);
?? if (result == false) throw;
}
}
void ReadFile(File f)
{
try
{
?? // 從文件中讀數(shù)據(jù),可能拋出FileReadException
}
catch(FileReadException& fe)
{
?? // 處理這個(gè)異常,如果這個(gè)異常可以很好的得以恢復(fù),那么處理完畢后函數(shù)
?? // 正常返回;否則必須重新拋出這個(gè)異常,以供上層的調(diào)用函數(shù)來(lái)能再次處
?? // 理這個(gè)異常對(duì)象
?? int result = ReReadFile(f);
?? if (result == false) throw;
}
}
void WriteFile(File f)
{
try
{
??? // 往文件中寫數(shù)據(jù),可能拋出FileWriteException
}
catch(FileWriteException& fe)
{
??? // 處理這個(gè)異常,如果這個(gè)異常可以很好的得以恢復(fù),那么處理完畢后函數(shù)
??? // 正常返回;否則必須重新拋出這個(gè)異常,以供上層的調(diào)用函數(shù)來(lái)能再次處理這個(gè)異常對(duì)象
??? int result = ReWriteFile(f);
??? if (result == false) throw;?
}?
}
void Func()
{
try
{
?? // 對(duì)文件進(jìn)行操作,可能出現(xiàn)FileWriteException、FileWriteException
?? // 和FileWriteException異常
?? OpenFile(…);
?? ReadFile(…);
?? WriteFile(…);
}
// 注意:FileException是FileOpenException、FileReadException和FileWriteException
// 的基類,因此這里定義的catch(FileException& fe)能捕獲所有與文件操作失敗的異
// 常。
catch(FileException& fe)
{
?? ExceptionInfo* ef = fe.GetExceptionInfo();
?? cout << “操作文件時(shí)出現(xiàn)了不可恢復(fù)的錯(cuò)誤,原因是:”<< fe << endl;
}
}
下面是更多面向?qū)ο蠛彤惓L幚斫Y(jié)合的例子:
#include <iostream.h>
class ExceptionClass
{
??? char* name;
public:
??? ExceptionClass(const char* name="default name")?
??? {
???????????? cout<<"Construct "<<name<<endl;
???????????? this->name=name;
??? }
?? ~ExceptionClass()
??? {
???????????? cout<<"Destruct "<<name<<endl;
??? }
??? void mythrow()
?? {
??????????? throw ExceptionClass("my throw");
?? }
}
void main()
{
?????? ExceptionClass e("Test");
?????? try
?????? {
?????????? e.mythrow();
?????? }??
?????? catch(...)
????? {
???????? cout<<”*********”<<endl;
?????? }
}
這是輸出信息:
Construct Test
Construct my throw
Destruct my throw
****************
Destruct my throw?? (這里是異常處理空間中對(duì)異常類的拷貝的析構(gòu))
Destruct Test
======================================
不過(guò)一般來(lái)說(shuō)我們可能更習(xí)慣于把會(huì)產(chǎn)生異常的語(yǔ)句和要throw的異常類分成不同的類來(lái)寫,下面的代碼可以是我們更愿意書寫的:
class ExceptionClass
{
public:
??? ExceptionClass(const char* name="Exception Default Class")
?? {
?????? cout<<"Exception Class Construct String"<<endl;
?? }
?? ~ExceptionClass()
?? {
????? cout<<"Exception Class Destruct String"<<endl;
?? }
?? void ReportError()
?? {
????? cout<<"Exception Class:: This is Report Error Message"<<endl;
?? }
};
class ArguClass
{
?? char* name;
public:
?? ArguClass(char* name="default name")
?? {
????? cout<<"Construct String::"<<name<<endl;
????? this->name=name;
?? }
?? ~ArguClass()
?? {
????? cout<<"Destruct String::"<<name<<endl;
?? }
?? void mythrow()
?? {
????? throw ExceptionClass("my throw");
?? }??????
};
_tmain()
{
?? ArguClass e("haha");
?? try
?? {
???? e.mythrow();
?? }
?? catch(int)
?? {
???? cout<<"If This is Message display screen, This is a Error!!"<<endl;? //這行不會(huì)執(zhí)行
?? }
?? catch(ExceptionClass pTest)
?? {
????? pTest.ReportError();
?? }
?? catch(...)
? {
?????? cout<<"***************"<<endl;??
? }
}
輸出Message:
Construct String::haha
Exception Class Construct String
Exception Class Destruct String
Exception Class:: This is Report Error Message
Exception Class Destruct String
Destruct String::haha
四、構(gòu)造和析構(gòu)中的異常拋出
先看個(gè)程序,假如我在構(gòu)造函數(shù)的地方拋出異常,這個(gè)類的析構(gòu)會(huì)被調(diào)用嗎?可如果不調(diào)用,那類里的東西豈不是不能被釋放了?
#include <iostream.h>
#include <stdlib.h>
class ExceptionClass1
{
?????? char* s;
public:
?????? ExceptionClass1()
????? {
????????????? cout<<"ExceptionClass1()"<<endl;
????????????? s=new char[4];
????????????? cout<<"throw a exception"<<endl;
????????????? throw 18;
?????? }
?????? ~ExceptionClass1()
????? {
????????????? cout<<"~ExceptionClass1()"<<endl;
????????????? delete[] s;
?????? }
};
void main()
{
?????? try
?????? {
???????????? ExceptionClass1 e;
?????? }
?????? catch(...)
?????? {}
}
結(jié)果為:
ExceptionClass1()
throw a exception
在這兩句輸出之間,我們已經(jīng)給S分配了內(nèi)存,但內(nèi)存沒(méi)有被釋放(因?yàn)樗窃谖鰳?gòu)函數(shù)中釋放的)。應(yīng)該說(shuō)這符合實(shí)際現(xiàn)象,因?yàn)閷?duì)象沒(méi)有
完整構(gòu)造。
為了避免這種情況,我想你也許會(huì)說(shuō):應(yīng)避免對(duì)象通過(guò)本身的構(gòu)造函數(shù)涉及到異常拋出。即:既不在構(gòu)造函數(shù)中出現(xiàn)異常拋出,也不應(yīng)在構(gòu)
造函數(shù)調(diào)用的一切東西中出現(xiàn)異常拋出。
但是在C++中可以在構(gòu)造函數(shù)中拋出異常,經(jīng)典的解決方案是使用STL的標(biāo)準(zhǔn)類auto_ptr。
那么,在析構(gòu)函數(shù)中的情況呢?我們已經(jīng)知道,異常拋出之后,就要調(diào)用本身的析構(gòu)函數(shù),如果這析構(gòu)函數(shù)中還有異常拋出的話,則已存在
的異常尚未被捕獲,會(huì)導(dǎo)致異常捕捉不到。
五、標(biāo)準(zhǔn)C++異常類
標(biāo)準(zhǔn)異常都派生自一個(gè)公共的基類exception。基類包含必要的多態(tài)性函數(shù)提供異常描述,可以被重載。下面是exception類的原型:
class exception
{
public:
??? exception() throw();
??? exception(const exception& rhs) throw();
??? exception& operator=(const exception& rhs) throw();
??? virtual ~exception() throw();
??? virtual const char *what() const throw();
};
?
C++有很多的標(biāo)準(zhǔn)異常類:
namespace std
{
??? //exception派生
??? class logic_error; //邏輯錯(cuò)誤,在程序運(yùn)行前可以檢測(cè)出來(lái)
?
??? //logic_error派生
??? class domain_error; //違反了前置條件
??? class invalid_argument; //指出函數(shù)的一個(gè)無(wú)效參數(shù)
??? class length_error; //指出有一個(gè)超過(guò)類型size_t的最大可表現(xiàn)值長(zhǎng)度的對(duì)象的企圖
??? class out_of_range; //參數(shù)越界
??? class bad_cast; //在運(yùn)行時(shí)類型識(shí)別中有一個(gè)無(wú)效的dynamic_cast表達(dá)式
??? class bad_typeid; //報(bào)告在表達(dá)試typeid(*p)中有一個(gè)空指針p
???
??? //exception派生
??? class runtime_error; //運(yùn)行時(shí)錯(cuò)誤,僅在程序運(yùn)行中檢測(cè)到
???
??? //runtime_error派生
??? class range_error; //違反后置條件
??? class overflow_error; //報(bào)告一個(gè)算術(shù)溢出
??? class bad_alloc; //存儲(chǔ)分配錯(cuò)誤
}
標(biāo)準(zhǔn)庫(kù)異常類定義在以下四個(gè)頭文件中
??? 1、exception頭文件:定義了最常見(jiàn)的標(biāo)準(zhǔn)異常類,其類名為exception。只通知異常的產(chǎn)生,但不會(huì)提供更多的信息
??? 2、stdexcept頭文件定義了以下幾種常見(jiàn)異常類
?? 函數(shù)?????????????????????????????????????????????? 功能或作用
exception???????????????????????????????????????????? 最常見(jiàn)的問(wèn)題
runtime_error???????????????????????????????????????? 運(yùn)行時(shí)錯(cuò)誤:僅在運(yùn)行時(shí)才能檢測(cè)到的問(wèn)題
range_error????????????????????????????????????????? 運(yùn)行時(shí)錯(cuò)誤:生成的結(jié)果超出了有意義的值域范圍
overflow_error??????????????????????????????????????? 運(yùn)行時(shí)錯(cuò)誤:計(jì)算上溢
underflow_error?????????????????????????????????????? 運(yùn)行時(shí)錯(cuò)誤:計(jì)算下溢
?
logic_error??????????????????????????????????????? 邏輯錯(cuò)誤:可在運(yùn)行前檢測(cè)到的問(wèn)題
domain_error?????????????????????????????????????? 邏輯錯(cuò)誤:參數(shù)的結(jié)果值不存在
invalid_argument?????????????????????????????????? 邏輯錯(cuò)誤:不合適的參數(shù)
length_error?????????????????????????????????????? 邏輯錯(cuò)誤:試圖生成一個(gè)超出該類型最大長(zhǎng)度的對(duì)象
out_of_range?????????????????????????????????????? 邏輯錯(cuò)誤:使用一個(gè)超出有效范圍的值
??? 3、new頭文件定義了bad_alloc異常類型,提供因無(wú)法分配內(nèi)存而由new拋出的異常
??? 4、type_info頭文件定義了bad_cast異常類型(要使用type_info必須包含typeinfo頭文件)
??? 下面是使用異常類的例子:
??? 首先,我定義了幾個(gè)異常類,這些類也可以從標(biāo)準(zhǔn)異常類進(jìn)行派生,如下
class BadInitializers
{
public:
?BadInitializers() {}
};
class OutOfBounds
{
public:
?OutOfBounds(int i) { cout<<"Size "<<i<<" is illegal!!!"<<endl; }
};
class SizeMismatch
{
public:
?SizeMismatch() {}
};
?
然后要在程序中需要的地方使用throw來(lái)拋出異常類,兩個(gè)拋出異常類的例子如下
template <class T>
Array1D<T>::Array1D(int sz)
{
? if(sz<0)
? {
??? //throw BadInitializers();
??? throw invalid_argument("Size has to be bigger than 0!!!");
? }
?size=sz;
?element=new T[size];
}
template <class T>
T &Array1D<T>::operator[](int i) const
{
?if(i<0||i>=size)
?{
??? throw OutOfBounds(i);
?}
?return element[i];
}
?
然后在主程序中使用try...catch...來(lái)捕獲異常,并進(jìn)行相應(yīng)的處理,如下
try
{
? int i=0;
? Array1D<int> a1(5);
? a1[0]=1;
? a1[1]=3;
? a1[2]=5;
? a1[3]=7;
? a1[4]=8;
? Array1D<int> a2(a1);
? for(i=0;i<a2.Size();i++)
? {
?? cout<<a2[i]<<" ";
? }
? cout<<endl;
? Array1D<int> a3(5);
? a3=a1+a2;
? cout<<a3;
}
?catch(BadInitializers)
?{
? cout<<"Error:BadInitializers!!!"<<endl;
?}
?catch(OutOfBounds &e)
?{
? cout<<"Error:OutOfBounds!!!"<<endl;
?}
?catch(SizeMismatch &e)
?{
? cout<<"Error:SizeMismatch!!!"<<endl;
?}
?catch(invalid_argument &e)
?{
? cout<<"Error:"<<e.what()<<endl;
?}
?catch(...)
?{
? cout<<"An unknown error!!!"<<endl;
?}
?
六、try finally使用
__try
{
?? file://保護(hù)塊
}
__finally
{
? file://結(jié)束處理程序
}
在上面的代碼段中,操作系統(tǒng)和編譯程序共同來(lái)確保結(jié)束處理程序中的__f i n a l l y代碼塊能夠被執(zhí)行,不管保護(hù)體(t r y塊)是如何
退出的。不論你在保護(hù)體中使用r e t u r n,還是g o t o,或者是longjump,結(jié)束處理程序(f i n a l l y塊)都將被調(diào)用。
我們來(lái)看一個(gè)實(shí)列:(返回值:10, 沒(méi)有Leak,性能消耗:小)
DWORD Func_SEHTerminateHandle()
{
DWORD dwReturnData = 0;
HANDLE hSem = NULL;
const char* lpSemName = "TermSem";
hSem =? CreateSemaphore(NULL, 1, 1, lpSemName);
__try
{
? WaitForSingleObject(hSem,INFINITE);
? dwReturnData = 5;
}
__finally
{
? ReleaseSemaphore(hSem,1,NULL);
? CloseHandle(hSem);
}
dwReturnData += 5;
return dwReturnData;
}
這段代碼應(yīng)該只是做為一個(gè)基礎(chǔ)函數(shù),我們將在后面修改它,來(lái)看看結(jié)束處理程序的作用:
====================
在代碼加一句:(返回值:5, 沒(méi)有Leak,性能消耗:中下)
DWORD Func_SEHTerminateHandle()
{
DWORD dwReturnData = 0;
HANDLE hSem = NULL;
const char* lpSemName = "TermSem";
hSem =? CreateSemaphore(NULL, 1, 1, lpSemName);
__try
{
? WaitForSingleObject(hSem,INFINITE);
? dwReturnData = 5;
? return dwReturnData;
}
__finally
{
? ReleaseSemaphore(hSem,1,NULL);
? CloseHandle(hSem);
}
dwReturnData += 5;
return dwReturnData;
}
在try塊的末尾增加了一個(gè)return語(yǔ)句。這個(gè)return語(yǔ)句告訴編譯程序在這里要退出這個(gè)函數(shù)并返回dwTemp變量的內(nèi)容,現(xiàn)在這個(gè)變量的值是
5。但是,如果這個(gè)return語(yǔ)句被執(zhí)行,該線程將不會(huì)釋放信標(biāo),其他線程也就不能再獲得對(duì)信標(biāo)的控制。可以想象,這樣的執(zhí)行次序會(huì)產(chǎn)生
很大的問(wèn)題,那些等待信標(biāo)的線程可能永遠(yuǎn)不會(huì)恢復(fù)執(zhí)行。
通過(guò)使用結(jié)束處理程序,可以避免return語(yǔ)句的過(guò)早執(zhí)行。當(dāng)return語(yǔ)句試圖退出try塊時(shí),編譯程序要確保finally塊中的代碼首先被執(zhí)行
。要保證finally塊中的代碼在try塊中的return語(yǔ)句退出之前執(zhí)行。在程序中,將ReleaseSemaphore的調(diào)用放在結(jié)束處理程序塊中,保證信
標(biāo)總會(huì)被釋放。這樣就不會(huì)造成一個(gè)線程一直占有信標(biāo),否則將意味著所有其他等待信標(biāo)的線程永遠(yuǎn)不會(huì)被分配CPU時(shí)間。
在finally塊中的代碼執(zhí)行之后,函數(shù)實(shí)際上就返回。任何出現(xiàn)在finally塊之下的代碼將不再執(zhí)行,因?yàn)楹瘮?shù)已在try塊中返回。所以這個(gè)函
數(shù)的返回值是5,而不是10。
讀者可能要問(wèn)編譯程序是如何保證在try塊可以退出之前執(zhí)行finally塊的。當(dāng)編譯程序檢查源代碼時(shí),它看到在try塊中有return語(yǔ)句。這樣
,編譯程序就生成代碼將返回值(本例中是5)保存在一個(gè)編譯程序建立的臨時(shí)變量中。編譯程序然后再生成代碼來(lái)執(zhí)行f i n a l l y塊中
包含的指令,這稱為局部展開(kāi)。更特殊的情況是,由于try塊中存在過(guò)早退出的代碼,從而產(chǎn)生局部展開(kāi),導(dǎo)致系統(tǒng)執(zhí)行finally塊中的內(nèi)容
。在finally塊中的指令執(zhí)行之后,編譯程序臨時(shí)變量的值被取出并從函數(shù)中返回。
可以看到,要完成這些事情,編譯程序必須生成附加的代碼,系統(tǒng)要執(zhí)行額外的工作。
?
finally塊的總結(jié)性說(shuō)明
我們已經(jīng)明確區(qū)分了強(qiáng)制執(zhí)行finally塊的兩種情況:
? 從try塊進(jìn)入finally塊的正常控制流。
? 局部展開(kāi):從try塊的過(guò)早退出(goto、long jump、continue、break、return等)強(qiáng)制控制轉(zhuǎn)移到finally塊。
第三種情況,全局展開(kāi)( global unwind),這個(gè)以后再看。
?
七、C++異常參數(shù)傳遞
從語(yǔ)法上看,在函數(shù)里聲明參數(shù)與在catch子句中聲明參數(shù)是一樣的,catch里的參數(shù)可以是值類型,引用類型,指針類型。例如:
try
{
?? .....
}
catch(A a)
{
}
catch(B& b)
{
}
catch(C* c)
{
}
? 盡管表面是它們是一樣的,但是編譯器對(duì)二者的處理卻又很大的不同。調(diào)用函數(shù)時(shí),程序的控制權(quán)最終還會(huì)返回到函數(shù)的調(diào)用處,但是拋
出一個(gè)異常時(shí),控制權(quán)永遠(yuǎn)不會(huì)回到拋出異常的地方。
class A;
void func_throw()
{
???? A a;
???? throw a;? //拋出的是a的拷貝,拷貝到一個(gè)臨時(shí)對(duì)象里
}
try
{
??? func_throw();
}
catch(A a)? //臨時(shí)對(duì)象的拷貝
{
}
當(dāng)我們拋出一個(gè)異常對(duì)象時(shí),拋出的是這個(gè)異常對(duì)象的拷貝。當(dāng)異常對(duì)象被拷貝時(shí),拷貝操作是由對(duì)象的拷貝構(gòu)造函數(shù)完成的。該拷貝構(gòu)造
函數(shù)是對(duì)象的靜態(tài)類型(static type)所對(duì)應(yīng)類的拷貝構(gòu)造函數(shù),而不是對(duì)象的動(dòng)態(tài)類型(dynamic type)對(duì)應(yīng)類的拷貝構(gòu)造函數(shù)。此時(shí)對(duì)象會(huì)
丟失RTTI信息。
異常是其它對(duì)象的拷貝,這個(gè)事實(shí)影響到你如何在catch塊中再拋出一個(gè)異常。比如下面這兩個(gè)catch塊,乍一看好像一樣:
catch (A& w) // 捕獲異常
{
// 處理異常
throw; // 重新拋出異常,讓它繼續(xù)傳遞
}
catch (A& w) // 捕獲Widget異常
{
// 處理異常
throw w; // 傳遞被捕獲異常的拷貝
}
? 第一個(gè)塊中重新拋出的是當(dāng)前異常(current exception),無(wú)論它是什么類型。(有可能是A的派生類)??
? 第二個(gè)catch塊重新拋出的是新異常,失去了原來(lái)的類型信息。
? 一般來(lái)說(shuō),你應(yīng)該用throw來(lái)重新拋出當(dāng)前的異常,因?yàn)檫@樣不會(huì)改變被傳遞出去的異常類型,而且更有效率,因?yàn)椴挥蒙梢粋€(gè)新拷貝。
看看以下這三種聲明:
catch (A w) ... // 通過(guò)傳值
catch (A& w) ... // 通過(guò)傳遞引用,一個(gè)被異常拋出的對(duì)象(總是一個(gè)臨時(shí)對(duì)象)可以通過(guò)普通的引用捕獲
catch (const A& w) ... //const引用
? 回到異常對(duì)象拷貝上來(lái)。我們知道,當(dāng)用傳值的方式傳遞函數(shù)的參數(shù),我們制造了被傳遞對(duì)象的一個(gè)拷貝,并把這個(gè)拷貝存儲(chǔ)到函數(shù)的參
數(shù)里。同樣我們通過(guò)傳值的方式傳遞一個(gè)異常時(shí),也是這么做的當(dāng)我們這樣聲明一個(gè)catch子句時(shí):
catch (A w) ... // 通過(guò)傳值捕獲
會(huì)建立兩個(gè)被拋出對(duì)象的拷貝,一個(gè)是所有異常都必須建立的臨時(shí)對(duì)象,第二個(gè)是把臨時(shí)對(duì)象拷貝進(jìn)w中。實(shí)際上,編譯器會(huì)優(yōu)化掉一個(gè)拷貝
。同樣,當(dāng)我們通過(guò)引用捕獲異常時(shí),
catch (A& w) ... // 通過(guò)引用捕獲
catch (const A& w) ... //const引用捕獲
這仍舊會(huì)建立一個(gè)被拋出對(duì)象的拷貝:拷貝是一個(gè)臨時(shí)對(duì)象。相反當(dāng)我們通過(guò)引用傳遞函數(shù)參數(shù)時(shí),沒(méi)有進(jìn)行對(duì)象拷貝。話雖如此,但是不是
所有編譯器都如此。
?
另外,通過(guò)指針拋出異常與通過(guò)指針傳遞參數(shù)是相同的。不論哪種方法都是一個(gè)指針的拷貝被傳遞。你不能認(rèn)為拋出的指針是一個(gè)指向局部
對(duì)象的指針,因?yàn)楫?dāng)異常離開(kāi)局部變量的生存空間時(shí),該局部變量已經(jīng)被釋放。Catch子句將獲得一個(gè)指向已經(jīng)不存在的對(duì)象的指針。這種行
為在設(shè)計(jì)時(shí)應(yīng)該予以避免。
另外一個(gè)重要的差異是在函數(shù)調(diào)用者或拋出異常者與被調(diào)用者或異常捕獲者之間的類型匹配的過(guò)程不同。在函數(shù)傳遞參數(shù)時(shí),如果參數(shù)不匹
配,那么編譯器會(huì)嘗試一個(gè)類型轉(zhuǎn)換,如果存在的話。而對(duì)于異常處理的話,則完全不是這樣。見(jiàn)一下的例子:
void func_throw()
{
???? CString a;
???? throw a;? //拋出的是a的拷貝,拷貝到一個(gè)臨時(shí)對(duì)象里
}
try
{
func_throw();
}
catch(const char* s)
{
}
拋出的是CString,如果用const char*來(lái)捕獲的話,是捕獲不到這個(gè)異常的。
盡管如此,在catch子句中進(jìn)行異常匹配時(shí)可以進(jìn)行兩種類型轉(zhuǎn)換。第一種是基類與派生類的轉(zhuǎn)換,一個(gè)用來(lái)捕獲基類的catch子句也可以處
理派生類類型的異常。反過(guò)來(lái),用來(lái)捕獲派生類的無(wú)法捕獲基類的異常。
第二種是允許從一個(gè)類型化指針(typed pointer)轉(zhuǎn)變成無(wú)類型指針(untyped pointer),所以帶有const void* 指針的catch子句能捕獲任何
類型的指針類型異常:
catch (const void*) ... //可以捕獲所有指針異常
另外,你還可以用catch(...)來(lái)捕獲所有異常,注意是三個(gè)點(diǎn)。
傳遞參數(shù)和傳遞異常間最后一點(diǎn)差別是catch子句匹配順序總是取決于它們?cè)诔绦蛑谐霈F(xiàn)的順序。因此一個(gè)派生類異常可能被處
理其基類異常的catch子句捕獲,這叫異常截獲,一般的編譯器會(huì)有警告。
class A {
public:
A()
{
cout << "class A creates" << endl;
}
void print()
{
cout << "A" << endl;
}
~A()
{
cout << "class A destruct" << endl;
}
};
class B: public A
{
public:
B()
{
cout << "class B create" << endl;
}
void print()
{
cout << "B" << endl;
}
~B()
{
cout << "class B destruct" << endl;
}
};
void func()
{
??? B b;
??? throw b;
}
try
{
??? func();
}
catch( B& b) //必須將B放前面,如果把A放前面,B放后面,那么B類型的異常會(huì)先被截獲。
{
??? b.print();
}
catch (A& a)
{
??? a.print() ;
}
----------------------------------------------------------------
1、基礎(chǔ)介紹
try
{
//程序中拋出異常
throw value;
}
catch(valuetype v)
{
//例外處理程序段
}
語(yǔ)法小結(jié):throw拋出值,catch接受,當(dāng)然,throw必須在“try語(yǔ)句塊”中才有效。
2、深入throw:
(i)、程序接受到throw語(yǔ)句后就會(huì)自動(dòng)調(diào)用析構(gòu)器,把該域(try后的括號(hào)內(nèi))對(duì)象clean up,然后再進(jìn)
入catch語(yǔ)句(如果在循環(huán)體中就退出循環(huán))。
這種機(jī)制會(huì)引起一些致命的錯(cuò)誤,比如,當(dāng)“類”有指針成員變量時(shí)(又是指針!),在 “類的構(gòu)建器
”中的throw語(yǔ)句引起的退出,會(huì)導(dǎo)致這個(gè)指針?biāo)赶虻膶?duì)象沒(méi)有被析構(gòu)。這里很基礎(chǔ),就不深入了,提
示一下,把指針改為類就行了,比如模板類來(lái)代替指針,在模板類的內(nèi)部設(shè)置一個(gè)析構(gòu)函數(shù)。
(ii)、語(yǔ)句“throw;”拋出一個(gè)無(wú)法被捕獲的異常,即使是catch(...)也不能捕捉到,這時(shí)進(jìn)入終止函數(shù)
,見(jiàn)下catch。
3、深入catch:
一般的catch出現(xiàn)的形式是:
try{}
catch(except1&){}
catch(except2&){}
catch(...){} //接受所有異常
一般都寫成引用(except1&),原因很簡(jiǎn)單,效率。
問(wèn)題a:拋出異常,但是catch不到異常怎么辦?(注意沒(méi)有java類似的finally語(yǔ)句)
在catch沒(méi)有捕獲到匹配的異常的時(shí)候,會(huì)調(diào)用默認(rèn)的終止函數(shù)。可以調(diào)用set_terminate()來(lái)設(shè)置終止函數(shù),參數(shù)是一個(gè)函數(shù)指針,類型是
:void (*terminate)()。
到這里,可以題個(gè)問(wèn)題:“沒(méi)有try-catch,直接在程序中"throw;",會(huì)怎么樣?”
其他一些技巧:
4、try一個(gè)函數(shù)體,形式如下
void fun(type1,type2) try----try放在函數(shù)體后
{
? 函數(shù)定義
}
catch(typeX){}
這個(gè)用法的效果就相當(dāng)于:
void fun()?
{
? try{函數(shù)定義}
}
5、throw一個(gè)函數(shù)體,形式如下:
void fun (); // 能拋出任何類型的異常
void fun () throw(except1,except2,except3)?
?????????????????? // 后面括號(hào)里面是一個(gè)異常參數(shù)表,本例中只能拋出這3中異常
void fun () throw()? // 參數(shù)表為空,不能拋出異常
問(wèn)題b:假設(shè)fun()中拋出了一個(gè)不在“異常參數(shù)表”中的異常,會(huì)怎么樣?
答:調(diào)用set_terminate()中設(shè)定的終止函數(shù)。然而,這只是表面現(xiàn)象,實(shí)際上是調(diào)用默認(rèn)的unexpected()函數(shù),然而這個(gè)默認(rèn)的
unexpected()調(diào)用了set_terminate()中設(shè)定的終止函數(shù)。可以用set_unexpected()來(lái)設(shè)置unexpected,就像set_terminate()一樣的用法,但
是在設(shè)定了新的“unexpected()”之后,就不會(huì)再調(diào)用set_terminater中設(shè)定的終止函數(shù)了。
這個(gè)語(yǔ)法是很有用的,因?yàn)樵谟脛e人的代碼時(shí),不知道哪個(gè)地方會(huì)調(diào)用什么函數(shù)又會(huì)拋出什么異常,用一個(gè)異常參數(shù)表在申明時(shí)限制一下,
很實(shí)用。
總結(jié)
以上是生活随笔為你收集整理的c++异常Try catch的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 利用WinPcap技术捕获数据包
- 下一篇: C++ 析构函数设为虚函数的好处