異常

異常提供了一種透過將控制權轉移到稱為處理程式的特殊函式來響應程式中異常情況(如執行時錯誤)的方法。

要捕獲異常,我們必須將一段程式碼置於異常檢查之下。這是透過將該程式碼段封裝在try塊中來完成的。當該塊內出現異常情況時,會丟擲一個異常,將控制權轉移到異常處理程式。如果沒有丟擲異常,程式碼將正常繼續,所有處理程式都將被忽略。

異常是透過在try塊內部使用throw關鍵字來丟擲的。異常處理程式使用關鍵字宣告catch,它必須緊跟在try塊之後。

// exceptions
#include <iostream>
using namespace std;

int main () {
  try
  {
    throw 20;
  }
  catch (int e)
  {
    cout << "An exception occurred. Exception Nr. " << e << endl;
  }
  return 0;
}
An exception occurred. Exception Nr. 20

受異常處理的程式碼被封裝在一個try塊中。在這個例子中,這段程式碼只是丟擲一個異常。

1
throw 20;

throw表示式接受一個引數(在本例中為整數值20),它作為引數傳遞給異常處理程式。

異常處理程式使用catch關鍵字宣告。如您所見,它緊跟在try塊的閉合花括號之後。catch的格式類似於一個常規函式,它至少有一個引數。此引數的型別非常重要,因為throw表示式傳遞的引數的型別會與它進行檢查,只有在它們匹配的情況下,異常才會被捕獲。

我們可以連結多個處理程式(catch表示式),每個處理程式具有不同的引數型別。只有型別與throw語句中指定的引數匹配的處理程式才會被執行。

如果我們使用省略號(...)作為catch的引數,該處理程式將捕獲任何型別的異常,而不管throw異常的型別是什麼。如果將其放在最後,可以將其用作捕獲所有未被其他處理程式捕獲的異常的預設處理程式。

1
2
3
4
5
6
try {
  // code here
}
catch (int param) { cout << "int exception"; }
catch (char param) { cout << "char exception"; }
catch (...) { cout << "default exception"; }

在這種情況下,最後一個處理程式將捕獲任何丟擲的異常,其引數不是int也不是char.

。在異常被處理之後,程式執行將從try-catch塊之後恢復,而不是從throw語句之後恢復!。

也可以將try-catch塊巢狀在更外部的try塊中。在這些情況下,我們有可能內部catch塊將異常轉發到其外部級別。這是透過表示式throw;(不帶引數)來完成的。例如,

1
2
3
4
5
6
7
8
9
10
11
try {
  try {
      // code here
  }
  catch (int n) {
      throw;
  }
}
catch (...) {
  cout << "Exception occurred";
}

異常規範


宣告函式時,我們可以透過在函式聲明後附加throw字尾來限制它可能直接或間接丟擲的異常型別。

1
float myfunction (char param) throw (int);

這聲明瞭一個名為myfunction的函式,該函式接受一個型別為char的引數,並返回一個型別為float的元素。此函式可能丟擲的唯一異常是型別為int的異常。如果它直接或間接丟擲了不同型別的異常,則無法被常規的int-type處理程式捕獲。

如果此throw說明符為空(沒有型別),則表示函式不允許丟擲異常。沒有throw說明符的函式(常規函式)可以丟擲任何型別的異常。

1
2
int myfunction (int param) throw(); // no exceptions allowed
int myfunction (int param);         // all exceptions allowed 

標準異常

C++標準庫提供了一個專門用於宣告要作為異常丟擲的物件的基類。它被稱為exception,定義在<exception>標頭檔案中,位於std名稱空間下。此類具有通常的預設和複製建構函式、運算子和解構函式,以及一個名為what的附加虛擬成員函式,該函式返回一個以null結尾的字元序列(char *),並且可以在派生類中重寫以包含某種異常描述。

// standard exceptions
#include <iostream>
#include <exception>
using namespace std;

class myexception: public exception
{
  virtual const char* what() const throw()
  {
    return "My exception happened";
  }
} myex;

int main () {
  try
  {
    throw myex;
  }
  catch (exception& e)
  {
    cout << e.what() << endl;
  }
  return 0;
}
My exception happened.

我們放置了一個透過引用捕獲異常物件的處理程式(注意型別後的&符號&),因此這也捕獲了派生自exception的類,例如我們的myex物件,它是myexception.

類的例項。C++標準庫的所有元件丟擲的異常都派生自這個std::exception類。它們是:

exception描述
bad_alloc由 new 在分配失敗時丟擲。
bad_cast由 dynamic_cast 在與引用型別失敗時丟擲。
bad_exception當異常型別不匹配任何 catch 時丟擲。
bad_typeid由 typeid 丟擲。
ios_base::failure由 iostream 庫中的函式丟擲。

例如,如果我們使用運算子new並且無法分配記憶體,則會丟擲型別為bad_alloc的異常。

1
2
3
4
5
6
7
8
try
{
  int * myarray= new int[1000];
}
catch (bad_alloc&)
{
  cout << "Error allocating memory." << endl;
}

建議將所有動態記憶體分配包含在 try 塊中,該塊捕獲此類異常,以執行乾淨的操作,而不是異常的程式終止,當此類異常被丟擲且未被捕獲時會發生這種情況。如果你想強制一個bad_alloc異常來看它的實際效果,你可以嘗試分配一個巨大的陣列;在我的系統上,嘗試分配 10 億int個 位元組 會丟擲bad_alloc異常。

因為bad_alloc派生自標準基類exception,我們可以透過捕獲exception

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// bad_alloc standard exception
#include <iostream>
#include <exception>
using namespace std;

int main () {
  try
  {
    int* myarray= new int[1000];
  }
  catch (exception& e)
  {
    cout << "Standard exception: " << e.what() << endl;
  }
  return 0;
}
 
Index
目錄