1、C++異常處理
創(chuàng)新互聯(lián)建站是一家專注網(wǎng)站建設(shè)、網(wǎng)絡(luò)營銷策劃、重慶小程序開發(fā)、電子商務(wù)建設(shè)、網(wǎng)絡(luò)推廣、移動(dòng)互聯(lián)開發(fā)、研究、服務(wù)為一體的技術(shù)型公司。公司成立10年以來,已經(jīng)為成百上千家成都酒店設(shè)計(jì)各業(yè)的企業(yè)公司提供互聯(lián)網(wǎng)服務(wù)?,F(xiàn)在,服務(wù)的成百上千家客戶與我們一路同行,見證我們的成長;未來,我們一起分享成功的喜悅。
(1)C++內(nèi)置了異常處理的語法元素,try...catch...,這是兩個(gè)新的關(guān)鍵字在C++中
@1:try語句代碼塊中用來處理正常代碼邏輯
@2:catch語句代碼塊中用來處理異常情況
@3:try語句中的異常由對(duì)應(yīng)的catch語句進(jìn)行處理
try
{
douuble r = divide(1, 0);
}
catch(...)
{
cout << "Divide by zero..." << endl;
}
@4:try語句代碼塊中用來處理可能發(fā)生異常的正常邏輯代碼,當(dāng)try代碼塊中的代碼發(fā)生了異常,就會(huì)拋出異常,catch語句就會(huì)捕捉到這個(gè)異常,進(jìn)入到catch語句中進(jìn)行處理這個(gè)異常。
(2)那么try代碼塊中的語句中是如何拋出異常的呢?
@1:C++通過throw關(guān)鍵字拋出異常信息
如:使用throw語句進(jìn)行異常拋出
double divide(double a, double b)
{
const double delta = 0.000000000001;
double ret = 0;
if ( !((-delta < b) && (b < delta)) )
{
ret = a / b;
}
else
{
throw 0;//產(chǎn)生除0異常,這里是0這個(gè)字面常量來代碼了當(dāng)前的異常元素,異常元素可以是字符串,可以是對(duì)象,可以是一個(gè)值等等
//當(dāng)程序執(zhí)行到throw時(shí),就會(huì)返回到調(diào)用這個(gè)divide函數(shù)的調(diào)用點(diǎn),try就會(huì)將這個(gè)異常元素轉(zhuǎn)給catch語句,catch語句塊就會(huì)抓住這個(gè)異常元素
}
return ret;
}
2、C++異常處理分析
(1)throw拋出的異常必須被catch處理
@1:當(dāng)前函數(shù)能夠處理異常,程序繼續(xù)往下執(zhí)行
@2:當(dāng)前函數(shù)如果無法處理收到的異常,則函數(shù)停止執(zhí)行,并返回
(2)未被處理的異常會(huì)順著函數(shù)調(diào)用棧向上傳播,直到被處理為止,否則程序?qū)⑼V箞?zhí)行。
比如:如果function1函數(shù)調(diào)用了function2函數(shù),function2函數(shù)調(diào)用了function3函數(shù),在function3函數(shù)中執(zhí)行執(zhí)行,并拋出了異常,也就是throw了,那么這個(gè)異常就會(huì)先看function3這個(gè)函數(shù)有沒有能力處理這個(gè)異常(也就是在沒在try中,有沒有對(duì)應(yīng)的catch處理的異常類型),如果有就進(jìn)行異常處理了,如果沒有function3這個(gè)函數(shù)就會(huì)立即停止執(zhí)行,并代碼著異常返回給function2調(diào)用function3函數(shù)的調(diào)用點(diǎn),如果function2沒有進(jìn)行處理(沒有在try中,也沒有catch處理的相關(guān)類型),function2就會(huì)立即停止執(zhí)行,帶著異常返回給function1函數(shù)調(diào)用function2函數(shù)的調(diào)用點(diǎn)。如果都沒有對(duì)throw扔出的這個(gè)異常元素進(jìn)行異常處理的話(函數(shù)調(diào)用沒在try中也沒有對(duì)應(yīng)的catch異常處理的相關(guān)類型),整個(gè)程序就會(huì)放棄執(zhí)行
例:throw拋出異常,對(duì)異常進(jìn)行處理,try...catch
#include
#include
using namespace std;
double divide(double a, double b)
{
const double delta = 0.00000000001;
double ret = 0;
if ( !((-delta < b) && (b < delta)) )
{
ret = a / b;
}
else
{
throw 0;//產(chǎn)生除0異常,這里是0這個(gè)int類型的值來代碼了當(dāng)前的異常元素,異常元素可以是字符串,可以是對(duì)象,可以是一個(gè)值等等
//當(dāng)程序執(zhí)行到throw時(shí),就會(huì)返回到調(diào)用這個(gè)divide函數(shù)的調(diào)用點(diǎn),try就會(huì)將這個(gè)異常元素轉(zhuǎn)給catch語句,catch語句塊就會(huì)抓住這個(gè)異常元素 //如果沒有對(duì)應(yīng)的catch對(duì)這個(gè)異常進(jìn)行處理,程序?qū)?huì)放棄執(zhí)行
}
return ret;
}
int main(int argc, char *argv[])
{
double num = 0;
try
{
num = divide(1, 1);//執(zhí)行到divide函數(shù)throw語句時(shí),就會(huì)返回throw語句后面的異常元素給這個(gè)try,這個(gè)try將異常元素給了catch,catch對(duì)這個(gè)異常進(jìn)行處理。
//如果沒有對(duì)異常進(jìn)行處理的操作,但你throw還拋出了異常,程序會(huì)停止運(yùn)行
cout << "num = " << num << endl;
}
catch(...)
{
cout << "Divide by zero ...." << endl;
}
return 0;
}
(3)同一個(gè)try語句可以跟上多個(gè)catch語句
@1:catch語句可以定義具體處理的異常類型
@2:不同類型的異常由不同的catch語句負(fù)責(zé)處理
@3:try語句中可以拋出任何類型的異常
@4:catch(...)用于處理所有類型的異常,并且這個(gè)catch(...)里面是3個(gè)點(diǎn)的catch語句塊只能放在最后catch處理的情況,當(dāng)有其他catch存在時(shí)。
@5:任何異常都只能被捕獲(catch)一次
(4)異常處理的匹配原則
try
{
throw 1;
}
catch (Type1 t1)
{
}
catch (Type2 t2)
{
}
catch (TypeN tn)
{
}
catch (...)//這個(gè)catch,處理任何類型的異常,當(dāng)有其他catch存在時(shí),只能作為最后的catch處理情況
{
}
異常拋出后,至上而下嚴(yán)格的匹配每一個(gè)catch語句處理的類型。異常處理匹配時(shí),不進(jìn)行任何的類型轉(zhuǎn)換。所以是嚴(yán)格匹配的。
如果當(dāng)前拋出異常的函數(shù)沒有對(duì)這個(gè)異常處理,就會(huì)沿著當(dāng)前函數(shù)的調(diào)用棧,順序的返回,直到被處理,如果都沒有進(jìn)行這個(gè)異常處理,程序就會(huì)停止執(zhí)行
例:一個(gè)try拋出異常,多個(gè)catch進(jìn)行異常類型匹配處理異常的情況
#include
#include
/*
*一個(gè)try中拋出異常,多個(gè)catch進(jìn)行匹配,
*匹配原則是嚴(yán)格的類型匹配,不會(huì)進(jìn)行類型的轉(zhuǎn)換,
*至上而下的進(jìn)行匹配catch中的類型,catch(...)只能放在對(duì)try中拋出異常的處理最后
*/
using namespace std;
void Demo1()
{
try
{
throw 0;//直接拋出異常,這個(gè)是int類型的
}
catch (char c)
{
cout << "catch (char c)" << endl;
}
catch (double d)
{
cout << "catch (double d)" << endl;
}
catch (string s)
{
cout << "catch (string s)" << endl;
}
catch (int i)
{
cout << "catch (int i)" << endl;
}
catch (...)
{
cout << "catch (...)" << endl;
}
}
void Demo2()
{
try
{
throw "haha";//這個(gè)const char* 類型的
}
catch (char *s)
{
cout << "catch (char *s)" << endl;
}
catch (string ss)
{
cout << "catch (string ss)" << endl;
}
catch (const char * cs)
{
cout << "catch (const char * cs)" << endl;
}
}
int main(int argc, char *argv[])
{
Demo1();
try
{
Demo2();
}
catch (...)
{
cout << "catch (...)" << endl;
}
return 0;
}
最后的執(zhí)行結(jié)果,會(huì)打印catch (int i)和catch (...)
3、catch語句塊中也可以拋出異常
try
{
func();
}
catch(int i)
{
throw i;//將捕獲到的異常重新拋出。
}
catch(...)
{
throw; //將捕獲到的異常重新拋出
}
catch中拋出的異常需要外層的try...catch...捕獲。
(1)C++中之所以支持catch語句塊中拋出異常,是因?yàn)槲覀冊(cè)诠こ涕_發(fā)中,會(huì)經(jīng)常的使用第三方庫進(jìn)行開發(fā),如果第三方庫中的func函數(shù)在使用時(shí)有可能會(huì)拋出異常,并且拋出的異常是-1,-2,-3等int類型異常,每一個(gè)異常元素對(duì)應(yīng)的意思可以看第三方庫中的文檔來知道,但是我們?cè)陂_發(fā)中,如果真遇到了第三方庫拋出了異常,但是我們確無法直觀的直接從這幾個(gè)-1,-2,-3異常元素來知道每一個(gè)異常元素對(duì)應(yīng)的是什么情況,只能去查第三方庫提供的文檔來知道,這是很浪費(fèi)時(shí)間的,所以我們?yōu)榱碎_發(fā)效率,所以我們將會(huì)將第三方庫拋出的異常,進(jìn)行統(tǒng)一的封裝,也就是將func函數(shù)在我們自己寫的Myfunc函數(shù)中調(diào)用,在Myfunc函數(shù)中,我們將第三方庫func函數(shù)中可能拋出的異常元素進(jìn)行重解釋在拋出,這樣我們就可以在工程開發(fā)中直接處理Myfunc這個(gè)函數(shù)拋出的重解釋了第三方庫func函數(shù)中拋出的異常。方便處理。
例:工程中在catch中拋出異常的用法,用于將第三方庫中提供的函數(shù)拋出的異常進(jìn)行重解釋。
#include
#include
using namespace std;
/*
假設(shè):func函數(shù)是第三方庫中提供的函數(shù),這個(gè)函數(shù)我們是無法修改的,因?yàn)槲覀兊貌坏皆创a一般情況下
一般情況下,我們用的是第三方庫提供的動(dòng)態(tài)鏈接庫。
func函數(shù)會(huì)拋出異常:
-1: 表示參數(shù)異常了
-2: 運(yùn)行異常
-3: 超時(shí)異常
當(dāng)這個(gè)func函數(shù)拋出異常的時(shí)候,我們無法直觀的從它拋出的異常來知道究竟是發(fā)生了什么情況,只能去查閱第三方的文檔。
所以為了方便,也為了架構(gòu)的考慮,因?yàn)槲覀冮_發(fā)時(shí),一般還有自己的私有庫,所以我們就會(huì)對(duì)這個(gè)第三方庫函數(shù)的異常進(jìn)行重解釋處理。
處理方法,就是我們自己寫一個(gè)MyFunc函數(shù),這個(gè)函數(shù)中調(diào)用了這個(gè)第三方庫func函數(shù),對(duì)這個(gè)func函數(shù)可能會(huì)拋出的異常進(jìn)行重解釋處理。
這樣,我們的工程在使用func函數(shù)出現(xiàn)異常的時(shí)候,就只是針對(duì)于Myfunc我們自己寫的這個(gè)函數(shù)的異常,同時(shí)異常的意思也被我們重解釋的更清晰了
*/
void func(int i)
{
if (i < 10)
{
throw -1;
}
else if (i == 11)
{
throw -2;
}
else if (i > 100)
{
throw -3;
}
}
void MyFunc(int i)//自己提供的函數(shù),完成和func一樣的功能,只是為了重解釋一下第三方庫func函數(shù)拋出的異常
{
try
{
func(i);
}
catch (int i)
{
switch (i) //對(duì)第三方func函數(shù)拋出的異常進(jìn)行重解釋。
{
case -1:
throw "Invalid Exception";
break;
case -2:
throw "RunException";
break;
case -3:
throw "Timeout Exceptin";
break;
}
}
}
int main(void)
{
try
{
MyFunc(101);
}
catch (const char *cs)
{
cout << "Exception Info: " << cs << endl;
}
return 0;
}
4、異常的類型可以是自定義的類類型
(1)對(duì)于類類型的匹配依然是至上而下的嚴(yán)格匹配
(2)賦值兼容性原則在異常匹配中依然適用(子類的異常對(duì)象,可以被父類的catch語句塊抓住)
(3)所以一般而言:
@1:匹配子類異常的catch放在上部
@2:匹配父類異常的catch放在下部
(5)在工程中會(huì)定義一系列的異常類
@1:每個(gè)類代表工程中可能出現(xiàn)的一種異常類型
@2:代碼復(fù)用時(shí)可能需要重解釋不同的異常類
@3:在定義catch語句塊時(shí)如果使用的異常是類對(duì)象,那么推薦使用引用作為參數(shù),因?yàn)檫@樣可以避開拷貝構(gòu)造,提高程序效率
例:用異常類對(duì)異常進(jìn)行重解釋
#include
#include
using namespace std;
/*
工程中一般會(huì)常使用異常類,自定義一個(gè)異常類,來表示出現(xiàn)異常時(shí)的詳細(xì)信息
*/
class Base
{
};
class Exception: public Base//繼承了Base,所以catch接受這個(gè)類拋出的異常時(shí),catch接受這個(gè)父類的異常處理要放到后面
{
private:
int m_id;//異常的ID號(hào),也就是第三方庫func函數(shù)中拋出異常的異常元素號(hào)。
string m_desc;//異常的信息描述
public:
Exception(int id, string desc)
{
m_id = id;
m_desc = desc;
}
int id() const
{
return m_id;
}
string description() const
{
return m_desc;
}
};
/*
假設(shè):func函數(shù)是第三方庫中提供的函數(shù),這個(gè)函數(shù)我們是無法修改的,因?yàn)槲覀兊貌坏皆创a一般情況下
一般情況下,我們用的是第三方庫提供的動(dòng)態(tài)鏈接庫。
func函數(shù)會(huì)拋出異常:
-1: 表示參數(shù)異常了
-2: 運(yùn)行異常
-3: 超時(shí)異常
當(dāng)這個(gè)func函數(shù)拋出異常的時(shí)候,我們無法直觀的從它拋出的異常來知道究竟是發(fā)生了什么情況,只能去查閱第三方的文檔。
所以為了方便,也為了架構(gòu)的考慮,因?yàn)槲覀冮_發(fā)時(shí),一般還有自己的私有庫,所以我們就會(huì)對(duì)這個(gè)第三方庫函數(shù)的異常進(jìn)行重解釋處理。
處理方法,就是我們自己寫一個(gè)MyFunc函數(shù),這個(gè)函數(shù)中調(diào)用了這個(gè)第三方庫func函數(shù),對(duì)這個(gè)func函數(shù)可能會(huì)拋出的異常進(jìn)行重解釋處理。
這樣,我們的工程在使用func函數(shù)出現(xiàn)異常的時(shí)候,就只是針對(duì)于Myfunc我們自己寫的這個(gè)函數(shù)的異常,同時(shí)異常的意思也被我們重解釋的更清晰了
*/
void func(int i)
{
if (i < 10)
{
throw -1;
}
else if (i == 11)
{
throw -2;
}
else if (i > 100)
{
throw -3;
}
}
void MyFunc(int i)//自己提供的函數(shù),完成和func一樣的功能,只是為了重解釋一下第三方庫func函數(shù)拋出的異常
{
try
{
func(i);
}
catch (int i)
{
switch (i) //對(duì)第三方func函數(shù)拋出的異常進(jìn)行重解釋。
{
case -1:
throw Exception(-1, "Invalid Exception");
break;
case -2:
throw Exception(-2, "Run Exception");
break;
case -3:
throw Exception(-3, "Timeout Exceptin");
break;
}
}
}
int main(void)
{
try
{
MyFunc(111);
}
catch (const Exception& e)
{
cout << "Exception Info: " << endl;
cout << "ID: " << e.id() << endl;
cout << "Description: " << e.description() << endl;
}
catch (const Base& e)//父類的接受異常要放到后面,因?yàn)橘x值兼容性原則,如果這個(gè)接受異常放在了前面,那么拋出的異常就會(huì)被父類接受到了
{
cout << "catch (const Base& e)" << endl;
}
return 0;
}
6、C++標(biāo)準(zhǔn)庫中提供了實(shí)用異常類族,使用時(shí)要包含
(1)標(biāo)準(zhǔn)庫中的異常都是從exception頂層父類派生的
(2)exception類有兩個(gè)主要分支,在于異常的類型是不一樣的
@1:logic_error
常用于程序中的可避免邏輯錯(cuò)誤,(out_of_range("可以有參數(shù),字符串參數(shù),只是哪個(gè)函數(shù)發(fā)生的異常");數(shù)組訪問越界,參數(shù)錯(cuò)誤等)
@2:runtime_error
常用于程序中無法避免的惡性錯(cuò)誤()