C++| |异常
異常
# 前言
異常是一種處理錯誤的方式。當一個函數發現自己無法處理的錯誤時就可以拋出異常,讓函數的直接或者間接調用者處理這個函數
-
throw:當問題出現時,程序會拋出一個異常。通過throw關鍵字來實現
-
catch:在想要處理問題的地方,通過異常處理程序捕獲異常,可以有多個catch捕獲
-
try:try塊中的代碼標識將被激活特定的異常。后面通常跟這一個或者多個catch塊
如果有一個塊中拋出異常的話,捕獲異常的方法是try和catch關鍵字。try塊中放置可能拋出異常的代碼。try塊中的代碼稱為保護代碼。
?
# 異常的使用
# 異常的拋出和捕獲
異常的拋出和匹配的原則
-
異常是通過拋出對象而引發的,該對象的類型決定要引發哪一個catch的處理代碼
-
被選中的處理代碼是調用鏈中和該對象類型匹配并且距離拋出異常位置最近的哪一個
-
拋出異常對象后,會生成一個異常對象的拷貝,因為拋出的異常對象有可能是一個臨時對象。所以會生成一個臨時對象,這個拷貝的臨時對象會在被catch之后銷毀
-
catch(...)可以捕獲各種類型的異常
-
實際中拋出和匹配的原則有著一個例外。可以拋出派生類對象,使用基類捕獲(實際中經常使用)
在函數調用棧中異常棧展開的原則
首先檢查throw本身是否在try的內部,如果是的話就查找匹配的catch語句,如果有匹配的就調用調用catch的處理代碼進行處理
沒有的話,那就退出當前的函數棧,繼續在調用函數的棧中進行查找匹配的catch
如果到達main函數的棧,還沒有找到匹配的catch,那就終止程序
找到匹配的catch子句并處理之后,需繼續沿著catch子句后面繼續執行
【注意】:
所以在實際情況當中我們都要加上一個catch(...)捕獲任意類型的異常,否則當有異常沒有被捕獲到的時候,程序就會終止
?
# 異常的重新拋出
有可能單個的catch不能完全處理一個異常,在進行一些校正處理之后,希望再次交給更外層的調用鏈函數來處理,catch則可以重新拋出異常交給上層的函數進行處理
例:
#include <iostream> #include <vector> ? using namespace std; ? double Division(int x, int y) {if (y == 0){throw "Divison by zero condition";} ?return (double)x / (double)y; } ? void Func() {int* array = new int[10];try {int x, y;std::cin >> x >> y;std::cout << Division(x, y) << std::endl;}catch(...){cout << "delete[]" << array << std::endl; delete[] array;throw;}std::cout << "delete[]" << array << std::endl;delete[] array; } ? int main() {try {Func();}catch(const char* errmsg){std::cout << errmsg <<std::endl;}catch(...){std::cout << "unkown exception!" << std::endl;}return ?0; }?
# 異常安全
-
構造函數通常完成對象的初始化,不要再構造函數中進行拋異常,有可能導致對象沒有初始化完全
-
析構函數通常完成資源的清理,不要再析構函數中進行拋異常,有可能導致資源泄露
-
C++異常會導致執行流的亂跳轉,所以就會導致資源泄露的問題。比如,在new和delete中拋出異常,導致內存泄露,在lock和unlock中拋出異常導致死鎖的問題。
?
# 異常的規范
異常規格說明的目的是為了讓函數使用者直到該函數可以拋出的異常有哪些
在函數的后面接throw(類型, ...),列出這個函數可能拋出的所有異常的類型
函數的后面接上throw(),表示這個函數不會拋出異常
若無異常接口聲明表示這個函數可能拋出任何類型的異常
例:
void Func() throw(const char*) //表明這個函數可能拋出const char*類型的異常void Func() throw() //表明這個函數不會拋出異常void Func() //表明這個函數可能拋出任何類型的異常?
# 異常的標準體系
-
自己定義的異常標準體系
-
對于一個公司來說的話一般都是自己定義一個異常標準體系。這樣的話大家拋出的都是派生類對象,然后捕獲一個基類就行了
-
-
C++標準庫異常體系
-
以父子類層次結構組織起來的
-
他們都是采用繼承的方式實現異常體系的,并且拋出對象的時候一般都是派生類對象采用基類對象來進行捕獲異常
?
# 異常的優缺點
優點:
異常對象定義好了,相比于錯誤碼可以清晰地展示出各種的錯誤信息,可以更好的定位程序的bug
對于錯誤碼的方式有著一個極大的缺點,那就是在函數調用鏈中,深層次的函數返回了錯誤,必須要層層返回錯誤,最外層才能拿到錯誤。異常可以進行執行流的跳轉
很多的第三方庫都包含異常。比如boost,gtest,gmock庫
很多測試框架都使用異常這樣才可以進行白盒測試
部分函數使用異常可以更好的處理,比如構造函數沒有返回
缺點:
異常會導致執行流的亂跳,并且運行時出錯異常就會亂流
C++沒有垃圾回收機制,資源需要自己進行管理,有了一場非常容易導致內存泄露,死鎖等異常安全問題
C++標準庫異常體系定義的不好,導致大家各自定義各自的異常體系,非常的混亂
異常盡量規范使用,否則后果不堪設想,隨意拋異常,外層捕獲的用戶苦不堪言。
異常規范
-
拋出異常類型都繼承自一個基類
-
函數是否拋出異常,拋出什么異常都采用Func() throw()的方式規范化
?
# 總結
異常總體來說,利大于弊,所以對于一個工程來說還是多用異常。并且OO(面向對象)的語言都是使用異常來處理錯誤。
大家可以看一下我寫的代碼:
https://github.com/YKitty/LinuxDir/tree/master/C%2B%2BCode/abnormal?
總結
- 上一篇: 简单几个步骤,通过github搭建浪漫的
- 下一篇: s3c2440移植MQTT