c++異常處理機(jī)制示例及詳細(xì)講解
這兩天我寫了一個(gè)測試c++異常處理機(jī)制的例子,感覺有很好的示范作用,在此貼出來,給c++異常處理的初學(xué)者入門。本文后附有c++異常的知識(shí)普及,有興趣者也可以看看。
下面的代碼直接貼到你的console工程中,可以運(yùn)行調(diào)試看看效果,并分析c++的異常機(jī)制。
#include "stdafx.h"
#include<stdlib.h>
#include<crtdbg.h>
#include <iostream>
// 內(nèi)存泄露檢測機(jī)制
#define _CRTDBG_MAP_ALLOC
#ifdef _DEBUG
#define new new(_NORMAL_BLOCK, __FILE__, __LINE__)
#endif
// 自定義異常類
class MyExcepction
{
public:
// 構(gòu)造函數(shù),參數(shù)為錯(cuò)誤代碼
MyExcepction(int errorId)
{
// 輸出構(gòu)造函數(shù)被調(diào)用信息
std::cout << "MyExcepction is called" << std::endl;
m_errorId = errorId;
}
// 拷貝構(gòu)造函數(shù)
MyExcepction( MyExcepction& myExp)
{
// 輸出拷貝構(gòu)造函數(shù)被調(diào)用信息
std::cout << "copy construct is called" << std::endl;
this->m_errorId = myExp.m_errorId;
}
~MyExcepction()
{
// 輸出析構(gòu)函數(shù)被調(diào)用信息
std::cout << "~MyExcepction is called" << std::endl;
}
// 獲取錯(cuò)誤碼
int getErrorId()
{
return m_errorId;
}
private:
// 錯(cuò)誤碼
int m_errorId;
};
int main(int argc, char* argv[])
{
// 內(nèi)存泄露檢測機(jī)制
_CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
// 可以改變錯(cuò)誤碼,以便拋出不同的異常進(jìn)行測試
int throwErrorCode = 110;
std::cout << " input test code :" << std::endl;
std::cin >> throwErrorCode;
try
{
if ( throwErrorCode == 110)
{
MyExcepction myStru(110);
// 拋出對象的地址 -> 由catch( MyExcepction* pMyExcepction) 捕獲
// 這里該對象的地址拋出給catch語句,不會(huì)調(diào)用對象的拷貝構(gòu)造函數(shù)
// 傳地址是提倡的做法,不會(huì)頻繁地調(diào)用該對象的構(gòu)造函數(shù)或拷貝構(gòu)造函數(shù)
// catch語句執(zhí)行結(jié)束后,myStru會(huì)被析構(gòu)掉
throw &myStru;
}
else if ( throwErrorCode == 119 )
{
MyExcepction myStru(119);
// 拋出對象,這里會(huì)通過拷貝構(gòu)造函數(shù)創(chuàng)建一個(gè)臨時(shí)的對象傳出給catch
// 由catch( MyExcepction myExcepction) 捕獲
// 在catch語句中會(huì)再次調(diào)用通過拷貝構(gòu)造函數(shù)創(chuàng)建臨時(shí)對象復(fù)制這里傳過去的對象
// throw結(jié)束后myStru會(huì)被析構(gòu)掉
throw myStru;
}
else if ( throwErrorCode == 120 )
{
// 不提倡這樣的拋出方法
// 這樣做的話,如果catch( MyExcepction* pMyExcepction)中不執(zhí)行delete操作則會(huì)發(fā)生內(nèi)存泄露
// 由catch( MyExcepction* pMyExcepction) 捕獲
MyExcepction * pMyStru = new MyExcepction(120);
throw pMyStru;
}
else
{
// 直接創(chuàng)建新對象拋出
// 相當(dāng)于創(chuàng)建了臨時(shí)的對象傳遞給了catch語句
// 由catch接收時(shí)通過拷貝構(gòu)造函數(shù)再次創(chuàng)建臨時(shí)對象接收傳遞過去的對象
// throw結(jié)束后兩次創(chuàng)建的臨時(shí)對象會(huì)被析構(gòu)掉
throw MyExcepction(throwErrorCode);
}
}
catch( MyExcepction* pMyExcepction)
{
// 輸出本語句被執(zhí)行信息
std::cout << "執(zhí)行了 catch( MyExcepction* pMyExcepction) " << std::endl;
// 輸出錯(cuò)誤信息
std::cout << "error Code : " << pMyExcepction->getErrorId()<< std::endl;
// 異常拋出的新對象并非創(chuàng)建在函數(shù)棧上,而是創(chuàng)建在專用的異常棧上,不需要進(jìn)行delete
//delete pMyExcepction;
}
catch ( MyExcepction myExcepction)
{
// 輸出本語句被執(zhí)行信息
std::cout << "執(zhí)行了 catch ( MyExcepction myExcepction) " << std::endl;
// 輸出錯(cuò)誤信息
std::cout << "error Code : " << myExcepction.getErrorId()<< std::endl;
}
catch(...)
{
// 輸出本語句被執(zhí)行信息
std::cout << "執(zhí)行了 catch(...) " << std::endl;
// 處理不了,重新拋出給上級
throw ;
}
// 暫停
int temp;
std::cin >> temp;
return 0;
}
知識(shí)點(diǎn): c++異常機(jī)制
一、 概述
C++自身有著非常強(qiáng)的糾錯(cuò)能力,發(fā)展到如今,已經(jīng)建立了比較完善的異常處理機(jī)制。C++的異常情況無非兩種,一種是語法錯(cuò)誤,即程序中出現(xiàn)了錯(cuò)誤的語句,函數(shù),結(jié)構(gòu)和類,致使編譯程序無法進(jìn)行。另一種是運(yùn)行時(shí)發(fā)生的錯(cuò)誤,一般與算法有關(guān)。
關(guān)于語法錯(cuò)誤,不必多說,寫代碼時(shí)心細(xì)一點(diǎn)就可以解決。C++編譯器的報(bào)錯(cuò)機(jī)制可以讓我們輕松地解決這些錯(cuò)誤。
第二種是運(yùn)行時(shí)的錯(cuò)誤,常見的有文件打開失敗、數(shù)組下標(biāo)溢出、系統(tǒng)內(nèi)存不足等等。而一旦出現(xiàn)這些問題,引發(fā)算法失效、程序運(yùn)行時(shí)無故停止等故障也是常有的。這就要求我們在設(shè)計(jì)軟件算法時(shí)要全面。比如針對文件打開失敗的情況,保護(hù)的方法有很多種,最簡單的就是使用“return”命令,告訴上層調(diào)用者函數(shù)執(zhí)行失?。涣硗庖环N處理策略就是利用c++的異常機(jī)制,拋出異常。
二、c++異常處理機(jī)制
C++異常處理機(jī)制是一個(gè)用來有效地處理運(yùn)行錯(cuò)誤的非常強(qiáng)大且靈活的工具,它提供了更多的彈性、安全性和穩(wěn)固性,克服了傳統(tǒng)方法所帶來的問題.
異常的拋出和處理主要使用了以下三個(gè)關(guān)鍵字: try、 throw 、 catch 。
拋出異常即檢測是否產(chǎn)生異常,在C++中,其采用throw語句來實(shí)現(xiàn),如果檢測到產(chǎn)生異常,則拋出異常。該語句的格式為:
throw 表達(dá)式;
如果在try語句塊的程序段中(包括在其中調(diào)用的函數(shù))發(fā)現(xiàn)了異常,且拋棄了該異常,則這個(gè)異常就可以被try語句塊后的某個(gè)catch語句所捕獲并處理,捕獲和處理的條件是被拋棄的異常的類型與catch語句的異常類型相匹配。由于C++使用數(shù)據(jù)類型來區(qū)分不同的異常,因此在判斷異常時(shí),throw語句中的表達(dá)式的值就沒有實(shí)際意義,而表達(dá)式的類型就特別重要。
try-catch語句形式如下 :
try
{
包含可能拋出異常的語句;
}
catch(類型名 [形參名]) // 捕獲特定類型的異常
{
}
catch(類型名 [形參名]) // 捕獲特定類型的異常
{
}
catch(...) // 三個(gè)點(diǎn)則表示捕獲所有類型的異常
{
}
【范例1】處理除數(shù)為0的異常。該范例將上述除數(shù)為0的異??梢杂胻ry/catch語句來捕獲異常,并使用throw語句來拋出異常,從而實(shí)現(xiàn)異常處理,實(shí)現(xiàn)代碼如代碼清單1-1所示。
// 代碼清單1-1
#include<iostream.h> //包含頭文件
#include<stdlib.h>
double fuc(double x, double y) //定義函數(shù)
{
if(y==0)
{
throw y; //除數(shù)為0,拋出異常
}
return x/y; //否則返回兩個(gè)數(shù)的商
}
void main()
{
double res;
try //定義異常
{
res=fuc(2,3);
cout<<"The result of x/y is : "<<res<<endl;
res=fuc(4,0); 出現(xiàn)異常,函數(shù)內(nèi)部會(huì)拋出異常
}
catch(double) //捕獲并處理異常
{
cerr<<"error of dividing zero.\n";
exit(1); //異常退出程序
}
}
【范例2】自定義異常類型 (在本文開始的代碼中已經(jīng)給出示范)
三、異常的接口聲明
為了加強(qiáng)程序的可讀性,使函數(shù)的用戶能夠方便地知道所使用的函數(shù)會(huì)拋出哪些異常,可以在函數(shù)的聲明中列出這個(gè)函數(shù)可能拋出的所有異常類型,例如:
void fun() throw( A,B,C,D);
這表明函數(shù)fun()可能并且只可能拋出類型(A,B,C,D)及其子類型的異常。
如果在函數(shù)的聲明中沒有包括異常的接口聲明,則此函數(shù)可以拋出任何類型的異常,例如: void fun();
一個(gè)不會(huì)拋出任何類型異常的函數(shù)可以進(jìn)行如下形式的聲明:
void fun() thow();
五、異常處理中需要注意的問題
1. 如果拋出的異常一直沒有函數(shù)捕獲(catch),則會(huì)一直上傳到c++運(yùn)行系統(tǒng)那里,導(dǎo)致整個(gè)程序的終止
2. 一般在異常拋出后資源可以正常被釋放,但注意如果在類的構(gòu)造函數(shù)中拋出異常,系統(tǒng)是不會(huì)調(diào)用它的析構(gòu)函數(shù)的,處理方法是:如果在構(gòu)造函數(shù)中要拋出異常,則在拋出前要記得刪除申請的資源。
3. 異常處理僅僅通過類型而不是通過值來匹配的,所以catch塊的參數(shù)可以沒有參數(shù)名稱,只需要參數(shù)類型。
4. 函數(shù)原型中的異常說明要與實(shí)現(xiàn)中的異常說明一致,否則容易引起異常沖突。
5. 應(yīng)該在throw語句后寫上異常對象時(shí),throw先通過Copy構(gòu)造函數(shù)構(gòu)造一個(gè)新對象,再把該新對象傳遞給 catch.
那么當(dāng)異常拋出后新對象如何釋放?
異常處理機(jī)制保證:異常拋出的新對象并非創(chuàng)建在函數(shù)棧上,而是創(chuàng)建在專用的異常棧上,因此它才可以跨接多個(gè)函數(shù)而傳遞到上層,否則在棧清空的過程中就會(huì)被銷毀。所有從try到throw語句之間構(gòu)造起來的對象的析構(gòu)函數(shù)將被自動(dòng)調(diào)用。但如果一直上溯到main函數(shù)后還沒有找到匹配的catch塊,那么系統(tǒng)調(diào)用terminate()終止整個(gè)程序,這種情況下不能保證所有局部對象會(huì)被正確地銷毀。
6. catch塊的參數(shù)推薦采用地址傳遞而不是值傳遞,不僅可以提高效率,還可以利用對象的多態(tài)性。另外,派生類的異常撲獲要放到父類異常撲獲的前面,否則,派生類的異常無法被撲獲。
7. 編寫異常說明時(shí),要確保派生類成員函數(shù)的異常說明和基類成員函數(shù)的異常說明一致,即派生類改寫的虛函數(shù)的異常說明至少要和對應(yīng)的基類虛函數(shù)的異常說明相同,甚至更加嚴(yán)格,更特殊。
相關(guān)文章
詳解C++虛函數(shù)中多態(tài)性的實(shí)現(xiàn)原理
C++是一種面向?qū)ο蟮木幊陶Z言,在C++中,虛函數(shù)是實(shí)現(xiàn)多態(tài)性的關(guān)鍵。本文就來探討一下C++虛函數(shù)中多態(tài)性的實(shí)現(xiàn)原理及其在面向?qū)ο缶幊讨械膽?yīng)用吧2023-05-05C++實(shí)現(xiàn)學(xué)生考勤信息管理系統(tǒng)
這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)學(xué)生考勤信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-12-12C++中的explicit關(guān)鍵字實(shí)例淺析
在C++程序中很少有人去使用explicit關(guān)鍵字,不可否認(rèn),在平時(shí)的實(shí)踐中確實(shí)很少能用的上,再說C++的功能強(qiáng)大,往往一個(gè)問題可以利用好幾種C++特性去解決。接下來給大家介紹 C++中的explicit關(guān)鍵字,需要的朋友可以參考下2017-03-03C++數(shù)據(jù)結(jié)構(gòu)之單鏈表的實(shí)現(xiàn)
線性表的鏈?zhǔn)酱鎯?chǔ)又稱為單鏈表,它是指通過一組任意的存儲(chǔ)單元來存儲(chǔ)線性表中的數(shù)據(jù)元素。本文將用C++實(shí)現(xiàn)單鏈表,需要的可以參考一下2022-05-05聊聊C++中右值引用和移動(dòng)構(gòu)造函數(shù)的使用
這篇文章主要是來和大家一起聊聊C++中右值引用和移動(dòng)構(gòu)造函數(shù)的使用,文中通過示例進(jìn)行了詳細(xì)講解,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2022-07-07C語言strlen,strcpy,strcmp,strcat,strstr字符串操作函數(shù)實(shí)現(xiàn)
這篇文章主要介紹了C語言strlen,strcpy,strcmp,strcat,strstr字符串操作函數(shù)實(shí)現(xiàn),,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-09-09