C++ 異常處理

C語言處理錯誤的方式

  • 終止程序。assert ,這種方式比較粗暴,如發生內存錯誤,除0錯誤時就會終止程序。
  • 返回錯誤碼,缺陷:需要程序員自己去查找對應的錯誤。如系統的很多庫的接口函數都是通過把錯誤碼放到errno中,表示錯誤。
  • C 標準庫中setjmp和longjmp組合。這個不是很常用,這個我也不是很瞭解。大家知道有這麼個東西就行了。

C++異常概念

異常是一種處理錯誤的方式,當一個函數發現自己無法處理的錯誤時就可以拋出異常,讓函數的直接或間接的調用者處理這個錯誤。

  • throw: 當問題出現時,程序會拋出一個異常。這是通過使用 throw 關鍵字來完成的。

  • catch: 在您想要處理問題的地方,通過異常處理程序捕獲異常.catch 關鍵字用於捕獲異常,可以有多 個catch進行捕獲。

  • try: try 塊中的代碼標識將被激活的特定異常,它後面通常跟着一個或多個 catch 塊。

具體用法如下:

try
{
 // 保護的標識代碼
}catch( Exception e1 )
{
 // catch 塊
}catch( Exception e2 )
{
 // catch 塊
}catch( Exception eN )
{
 // catch 塊
}

異常的使用

異常的拋出和匹配原則

1.異常是通過拋出對象而引發的,該對象的類型決定了應該激活哪個catch的處理代碼。
2. 被選中的處理代碼是調用鏈中與該對象類型匹配且離拋出異常位置最近的那一個。
3. 拋出異常對象後,會生成一個異常對象的拷貝,因爲拋出的異常對象可能是一個臨時對象,所以會生成一個拷貝對象,這個拷貝的臨時對象會在被catch以後銷燬。(這裏的處理類似於函數的傳值返回)
4. catch(…)可以捕獲任意類型的異常,問題是不知道異常錯誤是什麼。
5. 實際中拋出和捕獲的匹配原則有個例外,並不都是類型完全匹配,可以拋出的派生類對象,使用基類捕獲

double Division(int a, int b)
{
 // 當b == 0時拋出異常
 if (b == 0)
 throw "除0錯誤!";
 else
 return ((double)a / (double)b);
}
void Func()
{
 int len, time;
 cin >> len >> time;
 cout << Division(len, time) << endl;
}
int main()
{
 try {
 Func();
 }
 catch (const char* errmsg) {
 cout << errmsg << endl;
 }
 catch(...){
 cout<<"未知異常"<<endl;
 }
 return 0;
}

異常安全

  • 構造函數完成對象的構造和初始化,最好不要在構造函數中拋出異常,否則可能導致對象不完整或沒有 完全初始化
  • 析構函數主要完成資源的清理,最好不要在析構函數內拋出異常,否則可能導致資源泄漏(內存泄漏、句 柄未關閉等)
  • C++中異常經常會導致資源泄漏的問題,比如在new和delete中拋出了異常,導致內存泄漏,在lock和unlock之間拋出了異常導致死鎖,C++經常使用RAII來解決以上問題

異常規範

  1. 異常規格說明的目的是爲了讓函數使用者知道該函數可能拋出的異常有哪些。 可以在函數的後面接throw(類型),列出這個函數可能拋擲的所有異常類型。
  2. 函數的後面接throw(),表示函數不拋異常。
  3. 若無異常接口聲明,則此函數可以拋擲任何類型的異常。
// 這裏表示這個函數會拋出E1,E2,E3,E4中的某種類型的異常
void fun() throw(E1,E2,E3,E4);
// 這裏表示這個函數只會拋出bad_alloc的異常
void* operator new (std::size_t size) throw (std::bad_alloc);
// 這裏表示這個函數不會拋出異常
void* operator new (std::size_t size, void* ptr) throw();

C++異常標準

C++ 提供了一系列標準的異常,定義在 中,我們可以在程序中使用這些標準的異常。它們是以父子類層次結構組織起來的,如下所示:
在這裏插入圖片描述
下表是對上面層次結構中出現的每個異常的說明:

在這裏插入圖片描述
C++異常優缺點

優點:

  1. 相比於C語言返回錯誤碼的方式。異常更具合理性,它可以清晰的顯示出發生的錯誤的相關信息,有利於我們定位錯誤,找出bug,修改錯誤。
  2. 返回錯誤碼的傳統方式有個很大的問題就是,在函數調用鏈中,深層的函數返回了錯誤,那麼我們得層層返回錯誤,最外層才能拿到錯誤,而異常就很方便,直接會跳到你想捕獲的地方。
  3. 很多的第三方庫都包含異常,比如boost、gtest、gmock等等常用的庫,那麼我們使用它們也需要使用異常。
  4. 部分函數使用異常更好處理,比如構造函數沒有返回值,不方便使用錯誤碼方式處理。比如T&operator這樣的函數,如果pos越界了只能使用異常或者終止程序處理,沒辦法通過返回值表示錯誤。

缺點

  1. 異常會導致程序的執行流亂跳,並且非常的混亂,並且是運行時出錯拋異常就會亂跳。這會導致我們跟蹤調試時以及分析程序時,調試程序的時候就比較困難。
  2. 在 C++中 沒有垃圾回收機制,資源需要自己管理。有了異常就有可能容易導致內存泄漏、死鎖等異常安全問題。這個需要使用RAII來處理資源的管理問題。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章