C++中異常處理的基本思想及throw語(yǔ)句拋出異常的使用
異常處理基本思想
C++的異常處理的基本思想大致可以概括為傳統(tǒng)錯(cuò)誤處理機(jī)制、通過函數(shù)返回值來處理錯(cuò)誤。
1)C++的異常處理機(jī)制使得異常的引發(fā)和異常的處理不必在同一個(gè)函數(shù)中,這樣底層的函數(shù)可以著重解決具體問題,而不必過多的考慮異常的處理。上層調(diào)用者可以再適當(dāng)?shù)奈恢迷O(shè)計(jì)對(duì)不同類型異常的處理。
2)異常是專門針對(duì)抽象編程中的一系列錯(cuò)誤處理的,C++中不能借助函數(shù)機(jī)制,因?yàn)闂=Y(jié)構(gòu)的本質(zhì)是先進(jìn)后出,依次訪問,無(wú)法進(jìn)行跳躍,但錯(cuò)誤處理的特征卻是遇到錯(cuò)誤信息就想要轉(zhuǎn)到若干級(jí)之上進(jìn)行重新嘗試,如圖
3)異常超脫于函數(shù)機(jī)制,決定了其對(duì)函數(shù)的跨越式回跳。
4)異??缭胶瘮?shù)
異?;菊Z(yǔ)法
1) 若有異常則通過throw操作創(chuàng)建一個(gè)異常對(duì)象并拋擲。
2) 將可能拋出異常的程序段嵌在try塊之中??刂仆ㄟ^正常的順序執(zhí)行到達(dá)try語(yǔ)句,然后執(zhí)行try塊內(nèi)的保護(hù)段。
3) 如果在保護(hù)段執(zhí)行期間沒有引起異常,那么跟在try塊后的catch子句就不執(zhí)行。程序從try塊后跟隨的最后一個(gè)catch子句后面的語(yǔ)句繼續(xù)執(zhí)行下去。
4) catch子句按其在try塊后出現(xiàn)的順序被檢查。匹配的catch子句將捕獲并處理異常(或繼續(xù)拋擲異常)。
5) 如果匹配的處理器未找到,則運(yùn)行函數(shù)terminate將被自動(dòng)調(diào)用,其缺省功能是調(diào)用abort終止程序。
6)處理不了的異常,可以在catch的最后一個(gè)分支,使用throw語(yǔ)法,向上扔
7)異常機(jī)制與函數(shù)機(jī)制互不干涉,但捕捉的方式是基于類型匹配。捕捉相當(dāng)于函數(shù)返回類型的匹配,而不是函數(shù)參數(shù)的匹配,所以捕捉不用考慮一個(gè)拋擲中的多種數(shù)據(jù)類型匹配問題。
catch代碼塊必須出現(xiàn)在try后,并且在try塊后可以出現(xiàn)多個(gè)catch代碼塊,以捕捉各種不同類型的拋擲。
異常機(jī)制是基于這樣的原理:程序運(yùn)行實(shí)質(zhì)上是數(shù)據(jù)實(shí)體在做一些操作,因此發(fā)生異?,F(xiàn)象的地方,一定是某個(gè)實(shí)體出了差錯(cuò),該實(shí)體所對(duì)應(yīng)的數(shù)據(jù)類型便作為拋擲和捕捉的依據(jù)。
8)異常捕捉嚴(yán)格按照類型匹配
異常捕捉的類型匹配之苛刻程度可以和模板的類型匹配媲美,它不允許相容類型的隱式轉(zhuǎn)換,比如,拋擲char類型用int型就捕捉不到.例如下列代碼不會(huì)輸出“int exception.”,從而也不會(huì)輸出“That's ok.” 因?yàn)槌霈F(xiàn)異常后提示退出
int main(){ try{ throw ‘H'; } catch (int){ cout << "int exception.\n"; } cout << "That's ok.\n"; return 0; }
棧解旋(unwinding)
異常被拋出后,從進(jìn)入try塊起,到異常被拋擲前,這期間在棧上的構(gòu)造的所有對(duì)象,都會(huì)被自動(dòng)析構(gòu)。析構(gòu)的順序與構(gòu)造的順序相反。這一過程稱為棧的解旋(unwinding)。
#include <iostream> #include <cstdio> using namespace std; class MyException {}; class Test { public: Test(int a = 0, int b = 0) { this->a = a; this->b = b; cout << "Test 構(gòu)造函數(shù)執(zhí)行" << "a:" << a << " b: " << b << endl; } void printT() { cout << "a:" << a << " b: " << b << endl; } ~Test() { cout << "Test 析構(gòu)函數(shù)執(zhí)行" << "a:" << a << " b: " << b << endl; } private: int a; int b; }; void myFunc() throw (MyException) { Test t1; Test t2; cout << "定義了兩個(gè)棧變量,異常拋出后測(cè)試棧變量的如何被析構(gòu)" << endl; throw MyException(); } int main() { //異常被拋出后,從進(jìn)入try塊起,到異常被拋擲前,這期間在棧上的構(gòu)造的所有對(duì)象, //都會(huì)被自動(dòng)析構(gòu)。析構(gòu)的順序與構(gòu)造的順序相反。 //這一過程稱為棧的解旋(unwinding) try { myFunc(); } //catch(MyException &e) //這里不能訪問異常對(duì)象 catch (MyException) //這里不能訪問異常對(duì)象 { cout << "接收到MyException類型異常" << endl; } catch (...) { cout << "未知類型異常" << endl; } return 0; }
異常接口聲明
1)為了加強(qiáng)程序的可讀性,可以在函數(shù)聲明中列出可能拋出的所有異常類型,例如:
void func() throw (A, B, C , D); //這個(gè)函數(shù)func()能夠且只能拋出類型A B C D及其子類型的異常。
2)如果在函數(shù)聲明中沒有包含異常接口聲明,則次函數(shù)可以拋擲任何類型的異常,例如:
void func();
3)一個(gè)不拋擲任何類型異常的函數(shù)可以聲明為:
void func() throw();
4) 如果一個(gè)函數(shù)拋出了它的異常接口聲明所不允許拋出的異常,unexpected函數(shù)會(huì)被調(diào)用,該函數(shù)默認(rèn)行為調(diào)用terminate函數(shù)中止程序。
傳統(tǒng)處理錯(cuò)誤
#include <iostream> #include <cstdio> using namespace std; // 傳統(tǒng)的錯(cuò)誤處理機(jī)制 int myStrcpy(char *to, char *from) { if (from == NULL) { return 1; } if (to == NULL) { return 2; } // copy時(shí)的場(chǎng)景檢查 if (*from == 'a') { return 3; // copy時(shí)錯(cuò)誤 } while (*from != '\0') { *to = *from; to++; from++; } *to = '\0'; return 0; } int main() { int ret = 0; char buf1[] = "zbcdefg"; char buf2[1024] = { 0 }; ret = myStrcpy(buf2, buf1); if (ret != 0) { switch (ret) { case 1: cout << "源buf出錯(cuò)!\n"; break; case 2: cout << "目的buf出錯(cuò)!\n"; break; case 3: cout << "copy過程出錯(cuò)!\n"; break; default: cout << "未知錯(cuò)誤!\n"; break; } } cout << "buf2:\n" << buf2; cout << endl; return 0; }
throw char*
#include <iostream> #include <cstdio> using namespace std; // throw char * void myStrcpy(char *to, char *from) { if (from == NULL) { throw "源buf出錯(cuò)"; } if (to == NULL) { throw "目的buf出錯(cuò)"; } // copy時(shí)的場(chǎng)景檢查 if (*from == 'a') { throw "copy過程出錯(cuò)"; // copy時(shí)錯(cuò)誤 } while (*from != '\0') { *to = *from; to++; from++; } *to = '\0'; return; } int main() { int ret = 0; char buf1[] = "abcdefg"; char buf2[1024] = { 0 }; try { myStrcpy(buf2, buf1); } catch (int e) // e可以寫可以不寫 { cout << e << "int類型異常" << endl; } catch (char *e) { cout << "char* 類型異常" << endl; } catch (...) { }; cout << endl; return 0; }
throw 類對(duì)象
#include <iostream> #include <cstdio> using namespace std; class BadSrcType {}; class BadDestType {}; class BadProcessType { public: BadProcessType() { cout << "BadProcessType構(gòu)造函數(shù)do \n"; } BadProcessType(const BadProcessType &obj) { cout << "BadProcessType copy構(gòu)造函數(shù)do \n"; } ~BadProcessType() { cout << "BadProcessType析構(gòu)函數(shù)do \n"; } };
throw 類對(duì)象、類型異常
void my_strcpy3(char *to, char *from) { if (from == NULL) { throw BadSrcType(); } if (to == NULL) { throw BadDestType(); } //copy是的 場(chǎng)景檢查 if (*from == 'a') { printf("開始 BadProcessType類型異常 \n"); throw BadProcessType(); //會(huì)不會(huì)產(chǎn)生一個(gè)匿名對(duì)象? } if (*from == 'b') { throw &(BadProcessType()); //會(huì)不會(huì)產(chǎn)生一個(gè)匿名對(duì)象? } if (*from == 'c') { throw new BadProcessType; //會(huì)不會(huì)產(chǎn)生一個(gè)匿名對(duì)象? } while (*from != '\0') { *to = *from; to++; from++; } *to = '\0'; } int main() { int ret = 0; char buf1[] = "cbbcdefg"; char buf2[1024] = { 0 }; try { //my_strcpy1(buf2, buf1); //my_strcpy2(buf2, buf1); my_strcpy3(buf2, buf1); } catch (int e) //e可以寫 也可以不寫 { cout << e << " int類型異常" << endl; } catch (char *e) { cout << e << " char* 類型異常" << endl; } //--- catch (BadSrcType e) { cout << " BadSrcType 類型異常" << endl; } catch (BadDestType e) { cout << " BadDestType 類型異常" << endl; } //結(jié)論1: 如果 接受異常的時(shí)候 使用一個(gè)異常變量,則copy構(gòu)造異常變量. /* catch( BadProcessType e) //是把匿名對(duì)象copy給e 還是e還是那個(gè)匿名對(duì)象 { cout << " BadProcessType 類型異常" << endl; } */ /*結(jié)論2: 使用引用的話 會(huì)使用throw時(shí)候的那個(gè)對(duì)象 catch( BadProcessType &e) //是把匿名對(duì)象copy給e 還是e還是那個(gè)匿名對(duì)象 { cout << " BadProcessType 類型異常" << endl; } */ //結(jié)論3: 指針可以和引用/元素寫在一塊 但是引用和元素不能寫在一塊 catch (BadProcessType *e) //是把匿名對(duì)象copy給e 還是e還是那個(gè)匿名對(duì)象 { cout << " BadProcessType 類型異常" << endl; delete e; } //結(jié)論4: 類對(duì)象時(shí), 使用引用比較合適 // -- catch (...) { cout << "未知 類型異常" << endl; } return 0; }
相關(guān)文章
C語(yǔ)言實(shí)現(xiàn)一個(gè)文件版動(dòng)態(tài)通訊錄流程詳解
這篇文章主要介紹了C語(yǔ)言實(shí)現(xiàn)一個(gè)文件版動(dòng)態(tài)通訊錄流程,希望大家能從這篇文章中收獲到許多,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)吧2023-01-01Qt音視頻開發(fā)之利用ffmpeg實(shí)現(xiàn)倍速播放
這篇文章主要為大家詳細(xì)介紹了在Qt音視頻開發(fā)中如何利用ffmpeg實(shí)現(xiàn)倍速播放功能(半倍速/2倍速/4倍速/8倍速),感興趣的小伙伴可以了解一下2022-11-11C++多態(tài)的實(shí)現(xiàn)及原理詳細(xì)解析
C++的多態(tài)性用一句話概括就是:在基類的函數(shù)前加上virtual關(guān)鍵字,在派生類中重寫該函數(shù),運(yùn)行時(shí)將會(huì)根據(jù)對(duì)象的實(shí)際類型來調(diào)用相應(yīng)的函數(shù)。如果對(duì)象類型是派生類,就調(diào)用派生類的函數(shù);如果對(duì)象類型是基類,就調(diào)用基類的函數(shù)2013-09-09C++實(shí)現(xiàn)動(dòng)態(tài)順序表(vector)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)動(dòng)態(tài)順序表,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-05-05