網(wǎng)站首頁(yè) 編程語(yǔ)言 正文
一、異常的引入
傳統(tǒng)的C語(yǔ)言處理異常的方式有兩種:
1.終止程序:使用assert斷言語(yǔ)句,如果發(fā)生內(nèi)存錯(cuò)誤等,比如內(nèi)存泄漏或者除0錯(cuò)誤,都會(huì)直接終止程序。
2.返回錯(cuò)誤碼:通過(guò)錯(cuò)誤碼判斷發(fā)生的異常的類(lèi)型是什么,如系統(tǒng)的很多庫(kù)的接口程序通過(guò)把錯(cuò)誤碼放到errno中,表示錯(cuò)誤。
在實(shí)際中的C語(yǔ)言程序基本都是通過(guò)返回錯(cuò)誤碼的方式來(lái)處理錯(cuò)誤的,部分情況下使用終止程序來(lái)處理比較嚴(yán)重的錯(cuò)誤。
二、C++異常的關(guān)鍵字
目前市面上的大部分的主流語(yǔ)言都是使用異常機(jī)制來(lái)處理錯(cuò)誤的,當(dāng)一個(gè)函數(shù)發(fā)現(xiàn)自己無(wú)法處理一些錯(cuò)誤的時(shí)候會(huì)進(jìn)行拋異常處理,讓函數(shù)直接跳轉(zhuǎn)到捕獲異常的地方去處理異常。
下面介紹C++處理異常的幾個(gè)關(guān)鍵字:
throw:當(dāng)問(wèn)題出現(xiàn)的時(shí)候,程序會(huì)拋出一個(gè)異常,這是通過(guò)throw關(guān)鍵字來(lái)完成的。
catch:通過(guò)異常處捕獲異常,catch塊主要用于處理異常,或者執(zhí)行一些其他的操作。
try:try塊中的代碼標(biāo)識(shí)將被激活的特定異常,它的后面一般會(huì)跟一個(gè)catch塊。
三、異常的拋出與處理規(guī)則
double Disvision(int a, int b)
{
if (b == 0)
{
throw "Disvision by zero condition";
}
else
{
return ((double)a / (double)b);
}
}
void Func()
{
int len, time;
cin >> len >> time;
cout << Disvision(len, time) << endl;
}
int main()
{
try
{
Func();
}
catch(const char* errmsg)
{
cout << errmsg << endl;
}
catch (...)
{
cout << "unkown exception" << endl;
}
return 0;
}
當(dāng)向time傳入的值為0的時(shí)候,調(diào)用throw拋出異常,catch會(huì)捕獲到該異常進(jìn)行處理。
1.異常是通過(guò)拋出對(duì)象而引發(fā)的,該對(duì)象的類(lèi)型決定了應(yīng)該激活哪一個(gè)catch的處理代碼。
2.被選中的處理代碼是調(diào)用鏈中與該對(duì)象類(lèi)型匹配且離拋出異常位置最近的一個(gè)。
在這段程序中,有一個(gè)調(diào)用鏈:main()->Func()->Disvision(),在Disvision中拋出異常就會(huì)沿著調(diào)用鏈尋找能捕獲異常的catch來(lái)執(zhí)行。假設(shè)Func()中有一個(gè)與main()中一模一樣的catch函數(shù),那么除0的異常就會(huì)優(yōu)先調(diào)用Func()中的catch,因?yàn)樗x著最近。
3.catch的異常其實(shí)是拋出對(duì)象的拷貝,因?yàn)檎嬲龗伋龅膶?duì)象在出作用域的時(shí)候就已經(jīng)被銷(xiāo)毀了,拷貝的對(duì)象在catch成功之后也會(huì)自動(dòng)銷(xiāo)毀。
4.catch(…)可以捕獲任意類(lèi)型的異常,但問(wèn)題是不知道異常錯(cuò)誤是什么。
5.實(shí)際中拋出和捕獲的匹配原則有一個(gè)例外,并不都是類(lèi)型完全匹配,可以?huà)伋龅呐缮?lèi)對(duì)象,使用基類(lèi)捕獲。(要引入多態(tài))
6.當(dāng)catch異常之后,會(huì)沿著catch后的子句繼續(xù)執(zhí)行,類(lèi)似goto,算是異常的一個(gè)缺陷。
四、異常缺陷的處理
double Disvision(int a, int b)
{
if (b == 0)
{
throw "Disvision by zero condition";
}
else
{
return ((double)a / (double)b);
}
}
void Func()
{
int* array = new int[10];
int len, time;
cin >> len >> time;
cout << Disvision(len, time) << endl;
delete[] array;//一旦拋出異常,這里就不會(huì)被執(zhí)行
}
int main()
{
try
{
Func();
}
catch(const char* errmsg)
{
cout << errmsg << endl;
}
catch (...)
{
cout << "unkown exception" << endl;
}
return 0;
}
對(duì)于這段代碼而言,一旦Division拋出異常就會(huì)立刻執(zhí)行主函數(shù)中的catch,delete釋放內(nèi)存就不會(huì)被執(zhí)行。因此我們需要對(duì)這種情況進(jìn)行處理,即在Func中也捕獲一次異常,但是不對(duì)異常進(jìn)行處理,只是釋放空間:
void Func()
{
int* array = new int[10];
int len, time;
cin >> len >> time;
try {
cout << Disvision(len, time) << endl;
}
catch (const char* errmsg)
{
cout << "釋放空間" << endl;
delete[] array;
throw;
}
}
在釋放空間之后,直接調(diào)用throw表示將捕獲到的異常再拋出去。
五、自定義異常體系
class Exception
{
public:
Exception(const string& errmsg, int id)
:_errmsg(errmsg)
, _id(id)
{}
virtual string what() const
{
return _errmsg;
}
protected:
string _errmsg;
int _id;
};
class SqlException : public Exception
{
public:
SqlException(const string& errmsg, int id, const string& sql)
:Exception(errmsg, id)
, _sql(sql)
{}
virtual string what() const
{
string str = "SqlException:";
str += _errmsg;
str += "->";
str += _sql;
return str;
}
private:
const string _sql;
};
class CacheException : public Exception
{
public:
CacheException(const string& errmsg, int id)
:Exception(errmsg, id)
{}
virtual string what() const
{
string str = "CacheException:";
str += _errmsg;
return str;
}
};
class HttpServerException : public Exception
{
public:
HttpServerException(const string& errmsg, int id, const string& type)
:Exception(errmsg, id)
, _type(type)
{}
virtual string what() const
{
string str = "HttpServerException:";
str += _type;
str += ":";
str += _errmsg;
return str;
}
private:
const string _type;
};
void SQLMgr()
{
srand(time(0));
if (rand() % 7 == 0)
{
throw SqlException("權(quán)限不足", 100, "select * from name = '張三'");
}
//throw "xxxxxx";
}
void CacheMgr()
{
srand(time(0));
if (rand() % 5 == 0)
{
throw CacheException("權(quán)限不足", 100);
}
else if (rand() % 6 == 0)
{
throw CacheException("數(shù)據(jù)不存在", 101);
}
SQLMgr();
}
void HttpServer()
{
// ...
srand(time(0));
if (rand() % 3 == 0)
{
throw HttpServerException("請(qǐng)求資源不存在", 100, "get");
}
else if (rand() % 4 == 0)
{
throw HttpServerException("權(quán)限不足", 101, "post");
}
CacheMgr();
}
void ServerStart()
{
while (1)
{
this_thread::sleep_for(chrono::seconds(1));//休眠1s
try {
HttpServer();
}
catch (const Exception& e) // 這里捕獲父類(lèi)對(duì)象就可以
{
// 多態(tài)
cout << e.what() << endl;
}
catch (...)
{
cout << "Unkown Exception" << endl;
}
}
}
int main()
{
ServerStart();
return 0;
}
這里使用隨機(jī)數(shù)進(jìn)行模擬異常的拋出類(lèi)型,我們使用父類(lèi)捕獲異常,拋出子類(lèi)異常,使用多態(tài)調(diào)用子類(lèi)中的what()函數(shù)。
六、異常規(guī)范
1.異常規(guī)格說(shuō)明的目的是為了讓函數(shù)使用者知道。可以在函數(shù)的后面接throw類(lèi)(類(lèi)型),列出這個(gè)函數(shù)可能拋的所有異常類(lèi)型。
2.函數(shù)后面接throw(),表示函數(shù)不拋異常。
3.若無(wú)異常接口聲明,則此函數(shù)可以?huà)仈S任何類(lèi)型的異常。
void func() throw(A, B, C, D);//只會(huì)拋A/B/C/D中的某種類(lèi)型的異常
void* operator new(size_t size) throw(bad alloc);//這里表示這個(gè)函數(shù)只會(huì)拋出bad_alloc的異常
void* operator new(size_t size, void* ptr) throw();//這個(gè)函數(shù)不會(huì)拋異常
在C++11中,引入了noexception
bool Compare(int x, int y) noexcept(noexcept(x > y)) //C++11
{
return x > y;//表示如果x > y不發(fā)生異常,則Compare函數(shù)不會(huì)發(fā)生異常。
}
七、異常安全
構(gòu)造函數(shù)完成對(duì)象的初始化,最好不要在構(gòu)造函數(shù)中拋異常,否則可能導(dǎo)致對(duì)象不完整,或沒(méi)有完全初始化。
析構(gòu)函數(shù)主要完成資源的清理,最好不要在析構(gòu)函數(shù)中拋異常,否則可能導(dǎo)致資源泄漏(內(nèi)存泄漏,句柄未關(guān)閉)。
C++異常經(jīng)常會(huì)導(dǎo)致資源泄漏的問(wèn)題,比如在new和delete中拋出了異常,導(dǎo)致內(nèi)存泄漏,在lock和unlock之間拋出了異常導(dǎo)致死鎖,C++經(jīng)常使用RAII來(lái)解決以上問(wèn)題,關(guān)于RAII我們?cè)谥悄苤羔樦兄v解。
八、異常的優(yōu)缺點(diǎn)
1.優(yōu)點(diǎn)
1.相比錯(cuò)誤碼,更加清晰展示出錯(cuò)信息。
2.很多庫(kù)中包含異常,boost,gtest,gmock等常用的庫(kù),使用它們也需要異常。
3.部分函數(shù)使用異常更好處理,比如構(gòu)造函數(shù)沒(méi)有返回值,不方便使用錯(cuò)誤碼方式處理,比如T&operator這樣的函數(shù),只有一個(gè)返回值,如果pos越界了只能使用異常或者終止程序處理,沒(méi)辦法通過(guò)返回值表示錯(cuò)誤。
2.缺點(diǎn)
1.異常類(lèi)似goto,會(huì)導(dǎo)致程序的執(zhí)行流亂跳,并且非常混亂。
2.C++沒(méi)有垃圾回收機(jī)制,可能導(dǎo)致內(nèi)存泄漏。
3.各個(gè)公司的異常體系不同,有一定的學(xué)習(xí)成本。
原文鏈接:https://blog.csdn.net/qq_51492202/article/details/127006781
相關(guān)推薦
- 2022-12-21 使用RedisAtomicInteger計(jì)數(shù)出現(xiàn)少計(jì)問(wèn)題及解決_Redis
- 2022-08-10 pandas.DataFrame.from_dict直接從字典構(gòu)建DataFrame的方法_pyth
- 2022-08-30 Springcloud--Ribbon組件來(lái)實(shí)現(xiàn)服務(wù)調(diào)用的負(fù)載均衡
- 2022-07-02 ansible模塊之include_tasks:為什么加了tags后導(dǎo)入的任務(wù)沒(méi)有執(zhí)行?
- 2022-07-19 macOS Docker 內(nèi)存 CPU 占用過(guò)高,監(jiān)控到 com.Docker.hyperkit 進(jìn)
- 2022-09-26 RNN的手動(dòng)推導(dǎo)與代碼逐行實(shí)現(xiàn)
- 2022-05-08 react實(shí)現(xiàn)原生下拉刷新_React
- 2022-12-12 C語(yǔ)言使用函數(shù)實(shí)現(xiàn)字符串部分復(fù)制問(wèn)題_C 語(yǔ)言
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲(chǔ)小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過(guò)濾器
- Spring Security概述快速入門(mén)
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡(jiǎn)單動(dòng)態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對(duì)象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支