C++中異常處理的基本思想及throw語句拋出異常的使用
異常處理基本思想
C++的異常處理的基本思想大致可以概括為傳統(tǒng)錯誤處理機制、通過函數(shù)返回值來處理錯誤。

1)C++的異常處理機制使得異常的引發(fā)和異常的處理不必在同一個函數(shù)中,這樣底層的函數(shù)可以著重解決具體問題,而不必過多的考慮異常的處理。上層調(diào)用者可以再適當?shù)奈恢迷O計對不同類型異常的處理。
2)異常是專門針對抽象編程中的一系列錯誤處理的,C++中不能借助函數(shù)機制,因為棧結(jié)構的本質(zhì)是先進后出,依次訪問,無法進行跳躍,但錯誤處理的特征卻是遇到錯誤信息就想要轉(zhuǎn)到若干級之上進行重新嘗試,如圖

3)異常超脫于函數(shù)機制,決定了其對函數(shù)的跨越式回跳。
4)異??缭胶瘮?shù)
異?;菊Z法

1) 若有異常則通過throw操作創(chuàng)建一個異常對象并拋擲。
2) 將可能拋出異常的程序段嵌在try塊之中。控制通過正常的順序執(zhí)行到達try語句,然后執(zhí)行try塊內(nèi)的保護段。
3) 如果在保護段執(zhí)行期間沒有引起異常,那么跟在try塊后的catch子句就不執(zhí)行。程序從try塊后跟隨的最后一個catch子句后面的語句繼續(xù)執(zhí)行下去。
4) catch子句按其在try塊后出現(xiàn)的順序被檢查。匹配的catch子句將捕獲并處理異常(或繼續(xù)拋擲異常)。
5) 如果匹配的處理器未找到,則運行函數(shù)terminate將被自動調(diào)用,其缺省功能是調(diào)用abort終止程序。
6)處理不了的異常,可以在catch的最后一個分支,使用throw語法,向上扔
7)異常機制與函數(shù)機制互不干涉,但捕捉的方式是基于類型匹配。捕捉相當于函數(shù)返回類型的匹配,而不是函數(shù)參數(shù)的匹配,所以捕捉不用考慮一個拋擲中的多種數(shù)據(jù)類型匹配問題。
catch代碼塊必須出現(xiàn)在try后,并且在try塊后可以出現(xiàn)多個catch代碼塊,以捕捉各種不同類型的拋擲。
異常機制是基于這樣的原理:程序運行實質(zhì)上是數(shù)據(jù)實體在做一些操作,因此發(fā)生異常現(xiàn)象的地方,一定是某個實體出了差錯,該實體所對應的數(shù)據(jù)類型便作為拋擲和捕捉的依據(jù)。
8)異常捕捉嚴格按照類型匹配
異常捕捉的類型匹配之苛刻程度可以和模板的類型匹配媲美,它不允許相容類型的隱式轉(zhuǎn)換,比如,拋擲char類型用int型就捕捉不到.例如下列代碼不會輸出“int exception.”,從而也不會輸出“That's ok.” 因為出現(xiàn)異常后提示退出
int main(){
try{
throw ‘H';
}
catch (int){
cout << "int exception.\n";
}
cout << "That's ok.\n";
return 0;
}
棧解旋(unwinding)
異常被拋出后,從進入try塊起,到異常被拋擲前,這期間在棧上的構造的所有對象,都會被自動析構。析構的順序與構造的順序相反。這一過程稱為棧的解旋(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 構造函數(shù)執(zhí)行" << "a:" << a << " b: " << b << endl;
}
void printT()
{
cout << "a:" << a << " b: " << b << endl;
}
~Test()
{
cout << "Test 析構函數(shù)執(zhí)行" << "a:" << a << " b: " << b << endl;
}
private:
int a;
int b;
};
void myFunc() throw (MyException)
{
Test t1;
Test t2;
cout << "定義了兩個棧變量,異常拋出后測試棧變量的如何被析構" << endl;
throw MyException();
}
int main()
{
//異常被拋出后,從進入try塊起,到異常被拋擲前,這期間在棧上的構造的所有對象,
//都會被自動析構。析構的順序與構造的順序相反。
//這一過程稱為棧的解旋(unwinding)
try
{
myFunc();
}
//catch(MyException &e) //這里不能訪問異常對象
catch (MyException) //這里不能訪問異常對象
{
cout << "接收到MyException類型異常" << endl;
}
catch (...)
{
cout << "未知類型異常" << endl;
}
return 0;
}
異常接口聲明
1)為了加強程序的可讀性,可以在函數(shù)聲明中列出可能拋出的所有異常類型,例如:
void func() throw (A, B, C , D); //這個函數(shù)func()能夠且只能拋出類型A B C D及其子類型的異常。
2)如果在函數(shù)聲明中沒有包含異常接口聲明,則次函數(shù)可以拋擲任何類型的異常,例如:
void func();
3)一個不拋擲任何類型異常的函數(shù)可以聲明為:
void func() throw();
4) 如果一個函數(shù)拋出了它的異常接口聲明所不允許拋出的異常,unexpected函數(shù)會被調(diào)用,該函數(shù)默認行為調(diào)用terminate函數(shù)中止程序。
傳統(tǒng)處理錯誤
#include <iostream>
#include <cstdio>
using namespace std;
// 傳統(tǒng)的錯誤處理機制
int myStrcpy(char *to, char *from)
{
if (from == NULL) {
return 1;
}
if (to == NULL) {
return 2;
}
// copy時的場景檢查
if (*from == 'a') {
return 3; // copy時錯誤
}
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出錯!\n";
break;
case 2:
cout << "目的buf出錯!\n";
break;
case 3:
cout << "copy過程出錯!\n";
break;
default:
cout << "未知錯誤!\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出錯";
}
if (to == NULL) {
throw "目的buf出錯";
}
// copy時的場景檢查
if (*from == 'a') {
throw "copy過程出錯"; // copy時錯誤
}
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 類對象
#include <iostream>
#include <cstdio>
using namespace std;
class BadSrcType {};
class BadDestType {};
class BadProcessType
{
public:
BadProcessType()
{
cout << "BadProcessType構造函數(shù)do \n";
}
BadProcessType(const BadProcessType &obj)
{
cout << "BadProcessType copy構造函數(shù)do \n";
}
~BadProcessType()
{
cout << "BadProcessType析構函數(shù)do \n";
}
};
throw 類對象、類型異常
void my_strcpy3(char *to, char *from)
{
if (from == NULL)
{
throw BadSrcType();
}
if (to == NULL)
{
throw BadDestType();
}
//copy是的 場景檢查
if (*from == 'a')
{
printf("開始 BadProcessType類型異常 \n");
throw BadProcessType(); //會不會產(chǎn)生一個匿名對象?
}
if (*from == 'b')
{
throw &(BadProcessType()); //會不會產(chǎn)生一個匿名對象?
}
if (*from == 'c')
{
throw new BadProcessType; //會不會產(chǎn)生一個匿名對象?
}
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: 如果 接受異常的時候 使用一個異常變量,則copy構造異常變量.
/*
catch( BadProcessType e) //是把匿名對象copy給e 還是e還是那個匿名對象
{
cout << " BadProcessType 類型異常" << endl;
}
*/
/*結(jié)論2: 使用引用的話 會使用throw時候的那個對象
catch( BadProcessType &e) //是把匿名對象copy給e 還是e還是那個匿名對象
{
cout << " BadProcessType 類型異常" << endl;
}
*/
//結(jié)論3: 指針可以和引用/元素寫在一塊 但是引用和元素不能寫在一塊
catch (BadProcessType *e) //是把匿名對象copy給e 還是e還是那個匿名對象
{
cout << " BadProcessType 類型異常" << endl;
delete e;
}
//結(jié)論4: 類對象時, 使用引用比較合適
// --
catch (...)
{
cout << "未知 類型異常" << endl;
}
return 0;
}
相關文章
C語言自定義類型超詳細梳理之結(jié)構體 枚舉 聯(lián)合體
今天我們來學習一下自定義類型,自定義類型包括結(jié)構體、枚舉、聯(lián)合體,小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考2022-03-03
c++中struct和class的區(qū)別小結(jié)
在C++中,class和struct都是用于定義自定義數(shù)據(jù)類型的關鍵字,本文主要介紹了c++中struct和class的區(qū)別小結(jié),具有一定的參考價值,感興趣的可以了解一下2023-08-08
VC++中HTControl控件類的CHTSlider控制桿控件類簡介
這篇文章主要介紹了VC++中HTControl控件類的CHTSlider控制桿控件類,需要的朋友可以參考下2014-08-08

