C++基礎:異常處理


異常處理機制

程序運行時常會碰到一些異常情況,例如:

  • 做除法的時候除數爲 0;
  • 用戶輸入年齡時輸入了一個負數;
  • 用 new 運算符動態分配空間時,空間不夠導致無法分配;
  • 訪問數組元素時,下標越界;
  • 打開文件讀取時,文件不存在。

這些異常情況,如果不能發現並加以處理,很可能會導致程序崩潰。

所謂“處理”,可以是給出錯誤提示信息,然後讓程序沿一條不會出錯的路徑繼續執行;也可能是不得不結束程序,但在結束前做一些必要的工作,如將內存中的數據寫入文件、關閉打開的文件、釋放動態分配的內存空間等。

一發現異常情況就立即處理未必妥當,因爲在一個函數執行過程中發生的異常,在有的情況下由該函數的調用者決定如何處理更加合適。尤其像庫函數這類提供給程序員調用,用以完成與具體應用無關的通用功能的函數,執行過程中貿然對異常進行處理,未必符合調用它的程序的需要。

此外,將異常分散在各處進行處理不利於代碼的維護,尤其是對於在不同地方發生的同一種異常,都要編寫相同的處理代碼也是一種不必要的重複和冗餘。如果能在發生各種異常時讓程序都執行到同一個地方,這個地方能夠對異常進行集中處理,則程序就會更容易編寫、維護。

鑑於上述原因,C++ 引入了異常處理機制。其基本思想是:

  1. 函數 A 在執行過程中發現異常時可以不加處理,而只是“拋出一個異常”給 A 的調用者(假定爲函數 B),然後 A 立即中止;
  2. 在這種情況下,函數 B 可以選擇捕獲 A 拋出的異常進行處理,也可以選擇置之不理。如果置之不理,這個異常就會被拋給 B 的調用者,以此類推。
  3. 如果一層層的函數都不處理異常,異常最終會被拋給最外層的 main 函數。如果main函數也不處理異常,那麼程序就會立即中止。

異常拋出與捕獲

要處理異常,就需要捕獲異常。C++ 通過 try...catch 語句實現對異常的捕獲和處理。

try {
    語句組
}
catch(異常類型) {
    異常處理代碼
}
...
catch(異常類型) {
    異常處理代碼
}
// catch 可以有多個,但至少要有一個

try…catch 語句的執行過程是:

  • 如果在 try 塊執行的過程中沒有異常拋出,那麼執行完後就略過所有 catch 塊中的語句,繼續向下執行;
  • 如果 try 塊執行的過程中中拋出了異常,那麼拋出異常後立即跳轉到第一個和拋出的異常類型相匹配的 catch 塊中執行,稱作異常被該 catch 塊“捕獲”,在其中執行完異常處理程序後,再跳轉到最後一個 catch 塊後面繼續向下執行。因爲異常而未執行的語句組將不再執行。

除了C++運行時等底層自身拋出異常外,也可以通過throw主動拋出異常。

throw  表達式;	// 表達式的值可以是基本類型,也可以是類

異常類型

C++ 標準庫中有一些類代表異常,這些類都是從 exception 類派生而來的。常用的幾個異常類如下所示。
在這裏插入圖片描述
C++ 程序在碰到某些異常時,即使程序中沒有寫 throw 語句,也會自動拋出上述異常類的對象。這些異常類還都有名爲 what() 的成員函數,返回字符串形式的異常描述信息。使用這些異常類需要包含頭文件 <exception>

代碼示例:拋出並捕獲異常

捕獲通用異常

#include <exception>

try 
{
    // do something
}
catch (std::exception & e) 
{
    std::cerr <<"ERROR: "<< e.what() << std::endl;
}

捕獲特定異常 *

#include <exception>

try 
{
    // do something
}
catch (std::bad_cast & e) 
{
    std::cerr <<"ERROR: "<< e.what() << std::endl;
}

捕獲throw異常

#include <string>

try 
{
    if(判斷語句) throw std::string("Stack is empty!"); 
    if(判斷語句) throw -1;
    // do something
}
catch (std::string & e) 
{
    std::cerr <<"ERROR: "<< e << std::endl;
}
catch (int & e) 
{
    std::cerr <<"ERROR CODE: "<< e << std::endl;
}

注意:這種throw拋出的是基本類型,不能使用 std::exception 類來捕獲。

捕獲任何異常 *

try 
{
    // do something
}
catch (...) 
{
    std::cerr << "ERROR!" << std::endl;
}

捕獲自定義異常 *

#include <iostream>
#include <exception>

using namespace std;
 
struct MyException : public exception
{
  const char * what () const throw ()
  {
    return "C++ Exception";
  }
};
 
int main()
{
  try
  {
    throw MyException();
  }
  catch(MyException& e)
  {
    std::cout << "MyException caught" << std::endl;
    std::cout << e.what() << std::endl;
  }
  catch(std::exception& e)
  {
    //其他異常處理
  }
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章