C++異常捕捉與處理的深入講解
前言
在閱讀別人開發(fā)的項(xiàng)目中,也許你會(huì)經(jīng)??吹搅硕嗵幨褂卯惓5拇a,也許你也很少遇見使用異常處理的代碼。那在什么時(shí)候該使用異常,又在什么時(shí)候不該使用異常呢?在學(xué)習(xí)完異?;靖拍詈驼Z(yǔ)法之后,后面會(huì)有講解。
(1)異常拋出和捕捉語(yǔ)句
//1.拋出異常 throw 異常對(duì)象 //2.異常捕捉 try{ 可能會(huì)發(fā)生異常的代碼 }catch(異常對(duì)象){ 異常處理代碼 }
- throw子句:throw 子句用于拋出異常,被拋出的異常可以是C++的內(nèi)置類型(例如: throw int(1);),也可以是自定義類型。
- try區(qū)段:這個(gè)區(qū)段中包含了可能發(fā)生異常的代碼,在發(fā)生了異常之后,需要通過(guò)throw拋出。
- catch子句:每個(gè)catch子句都代表著一種異常的處理。catch子句用于處理特定類型的異常。catch塊的參數(shù)推薦采用地址傳遞而不是值傳遞,不僅可以提高效率,還可以利用對(duì)象的多態(tài)性。
(2)異常的處理規(guī)則
- throw拋出的異常類型與catch抓取的異常類型要一致;
- throw拋出的異常類型可以是子類對(duì)象,catch可以是父類對(duì)象;
- catch塊的參數(shù)推薦采用地址傳遞而不是值傳遞,不僅可以提高效率,還可以利用對(duì)象的多態(tài)性。另外,派生類的異常捕獲要放到父類異常撲獲的前面,否則,派生類的異常無(wú)法被撲獲;
- 如果使用catch參數(shù)中,使用基類捕獲派生類對(duì)象,一定要使用傳遞引用的方式,例如catch (exception &e);
- 異常是通過(guò)拋出對(duì)象而引發(fā)的,該對(duì)象的類型決定了應(yīng)該激活哪個(gè)處理代碼;
- 被選中的處理代碼是調(diào)用鏈中與該對(duì)象類型匹配且離拋出異常位置最近的那一個(gè);
- 在try的語(yǔ)句塊內(nèi)聲明的變量在外部是不可以訪問(wèn)的,即使是在catch子句內(nèi)也不可以訪問(wèn);
- 棧展開會(huì)沿著嵌套函數(shù)的調(diào)用鏈不斷查找,直到找到了已拋出的異常匹配的catch子句。如果拋出的異常一直沒有函數(shù)捕獲(catch),則會(huì)一直上傳到c++運(yùn)行系統(tǒng)那里,導(dǎo)致整個(gè)程序的終止。
(3)實(shí)例
實(shí)例1:拋出自定義類型異常。
class Data { public: Data() {} }; void fun(int n) { if(n==0) throw 0;//拋異常 int異常 if(n==1) throw "error"; //拋?zhàn)址惓? if(n==2) { Data data; throw data; } if(n>3) { throw 1.0; } } int main() { try { fun(6);//當(dāng)異常發(fā)生fun里面,fun以下代碼就不會(huì)再執(zhí)行,調(diào)到catch處執(zhí)行異常處理代碼,后繼續(xù)執(zhí)行catch以外的代碼。當(dāng)throw拋出異常后,沒有catch捕捉,則整個(gè)程序會(huì)退出,不會(huì)執(zhí)行整個(gè)程序的以下代碼 cout<<"*************"<<endl; }catch (int i) { cout<<i<<endl; }catch (const char *ptr) { cout<<ptr<<endl; }catch(Data &d) { cout<<"data"<<endl; }catch(...)//抓取 前面異常以外的所有其他異常 { cout<<"all"<<endl; } return 0; }
實(shí)例2:標(biāo)準(zhǔn)出錯(cuò)類拋出和捕捉異常。
#include <iostream> using namespace std; int main() { try { char* p = new char[0x7fffffff]; //拋出異常 } catch (exception &e){ cout << e.what() << endl; //捕獲異常,然后程序結(jié)束 } return 0; }
輸出結(jié)果:
當(dāng)使用new進(jìn)行開空間時(shí),申請(qǐng)內(nèi)存失敗,系統(tǒng)就會(huì)拋出異常,不用用戶自定義異常類型,此時(shí)捕獲到異常時(shí),就可告訴使用者是哪里的錯(cuò)誤,便于修改。
實(shí)例3:繼承標(biāo)準(zhǔn)出錯(cuò)類的派生類的異常拋出和捕捉。
#include <iostream> #include <exception> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> using namespace std; class FileException :public exception { public: FileException(string msg) { this->exStr = msg; } virtual const char*what() const noexcept//聲明這個(gè)函數(shù)不能再拋異常 { return this->exStr.c_str(); } protected: string exStr; }; void fun() { int fd = ::open("./open.txt",O_RDWR); if(fd<0) { FileException openFail("open fail"); //創(chuàng)建異常對(duì)象 throw openFail;//拋異常 } } int main( ) { try { fun(); } catch (exception &e) {//一般需要使用引用 cout<<e.what()<<endl; } cout<<"end"<<endl; return 0; }
當(dāng)文件不存在時(shí),輸出結(jié)果:
如果在Linux上運(yùn)行,上述代碼需要根據(jù)環(huán)境修改:
98標(biāo)準(zhǔn)寫法
~FileException()throw(){}//必須要 virtual const char*what() const throw()//聲明這個(gè)函數(shù)不能再拋異常 { return this->exStr.c_str(); } //編譯 g++ main.cpp
2011標(biāo)準(zhǔn)寫法
~FileException()noexcept{}//必須要 virtual const char*what() const noexcept//聲明這個(gè)函數(shù)不能再拋異常 { return this->exStr.c_str(); } //編譯 g++ main.cpp -std=c++11 指定用c++11標(biāo)準(zhǔn)編譯
(4)總結(jié)
1. 使用異常處理的優(yōu)點(diǎn):
傳統(tǒng)錯(cuò)誤處理技術(shù),檢查到一個(gè)錯(cuò)誤,只會(huì)返回退出碼或者終止程序等等,我們只知道有錯(cuò)誤,但不能更清楚知道是哪種錯(cuò)誤。使用異常,把錯(cuò)誤和處理分開來(lái),由庫(kù)函數(shù)拋出異常,由調(diào)用者捕獲這個(gè)異常,調(diào)用者就可以知道程序函數(shù)庫(kù)調(diào)用出現(xiàn)的錯(cuò)誤是什么錯(cuò)誤,并去處理,而是否終止程序就把握在調(diào)用者手里了。
2. 使用異常的缺點(diǎn):
如果使用異常,光憑查看代碼是很難評(píng)估程序的控制流:函數(shù)返回點(diǎn)可能在你意料之外,這就導(dǎo)致了代碼管理和調(diào)試的困難。啟動(dòng)異常使得生成的二進(jìn)制文件體積變大,延長(zhǎng)了編譯時(shí)間,還可能會(huì)增加地址空間的壓力。
C++沒有垃圾回收機(jī)制,資源需要自己管理。有了異常非常容易導(dǎo)致內(nèi)存泄漏、死鎖等異常安全問(wèn)題。 這個(gè)需要使用RAII來(lái)處理資源的管理問(wèn)題。學(xué)習(xí)成本較高。
C++標(biāo)準(zhǔn)庫(kù)的異常體系定義得不好,導(dǎo)致大家各自定義各自的異常體系,非常的混亂。
3. 什么時(shí)候使用異常?
建議:除非已有的項(xiàng)目或底層庫(kù)中使用了異常,要不然盡量不要使用異常,雖然提供了方便,但是開銷也大。
4. 程序所有的異常都可以catch到嗎?
并非如此,只有發(fā)生異常,并且又拋出異常的情況才能被catch到。例如,數(shù)組下標(biāo)訪問(wèn)越界的情況,系統(tǒng)是不會(huì)自身拋出異常的,所以我們無(wú)論怎么catch都是無(wú)效的;在這種情況,我們需要自定義拋出類型,判斷數(shù)組下標(biāo)是否越界,然后再根據(jù)自身需要throw自定義異常對(duì)象,這樣才可以catch到異常,并進(jìn)行進(jìn)一步處理。
到此這篇關(guān)于C++異常捕捉與處理的文章就介紹到這了,更多相關(guān)C++異常捕捉與處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語(yǔ)言類型轉(zhuǎn)換與常量的細(xì)節(jié)深入理解探究
這篇文章主要為大家介紹了C?語(yǔ)言類型轉(zhuǎn)換與常量的細(xì)節(jié)深入理解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-12-12C語(yǔ)言開發(fā)實(shí)現(xiàn)掃雷游戲
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言開發(fā)實(shí)現(xiàn)掃雷游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11C語(yǔ)言實(shí)現(xiàn)飛機(jī)大戰(zhàn)小游戲
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)飛機(jī)大戰(zhàn)小游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06C語(yǔ)言實(shí)現(xiàn)實(shí)驗(yàn)設(shè)備管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)實(shí)驗(yàn)設(shè)備管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-06-06C語(yǔ)言實(shí)現(xiàn)大整數(shù)加減運(yùn)算詳解
大數(shù)運(yùn)算,顧名思義,就是很大的數(shù)值的數(shù)進(jìn)行一系列的運(yùn)算。本文通過(guò)實(shí)例演示如何進(jìn)行C語(yǔ)言中的大整數(shù)加減運(yùn)算,有需要的可以參考借鑒。2016-08-08Cocos2dx實(shí)現(xiàn)數(shù)字跳動(dòng)效果
這篇文章主要為大家詳細(xì)介紹了Cocos2dx實(shí)現(xiàn)數(shù)字跳動(dòng)效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-09-09