- 動態分配空間時可能不會成功
- 打開文件可能會失敗
- 除法運算時分母可能爲0
- 整數相乘可能溢出
- 指針可能越界
- ……
- 使用選擇語句(if…else…)
- 判斷異常情況,即時處理
- 正常程序流程和異常處理語句混在一起
- 程序員往往無法專注於正常流程編程
- 使用C++異常處理機制
- 判斷異常情況,發現異常後拋出異常
- 正常程序流程和異常處理模塊分開
- 程序員可以專注於正常流程編程,異常處理模塊稍候編寫
- 程序在產生錯誤後拋出異常
- 異常處理模塊捕獲並處理異常
- 異常處理機制一般無法使程序恢復正常執行
- 可以爲程序提供有序的整理操作
- 關鍵字try:出錯時產生異常的代碼放在try塊中
- 關鍵字throw:throw語句可以拋出任意類型的異常,包括自定義類型
- 關鍵字catch:catch塊(異常處理器)捕捉和處理異常
- 一個異常處理器一般只捕捉一種類型的異常
- try塊拋出異常後,程序控制離開try塊
- 拋出異常後,程序在try塊後面的catch塊中逐個搜索合適的異常處理器
- 如果try塊沒有異常拋出,則程序跳過所有catch塊
- 拋出異常之後,程序控制無法返回到拋出點
- try塊可以直接或間接拋出異常
// ex17_1.cpp: 除數爲零的異常例子
#include <iostream>
#include <string>
using namespace std;
//定義異常類MyException
class MyException
{
public:
MyException(char *str)
{ msg = str; }
char * show()
{ return msg; }
private:
char *msg;
};
//定義除法函數division,除數爲0時拋出異常。
double division(int dividend, int divisor)
{
if (divisor == 0)
//拋出異常對象
throw MyException("error: divided by zero!");
return (double)dividend/divisor;
}
int main()
{
int a, b;
double result;
cout<<"Enter two integers (EOF to end):";
while (cin>>a>>b){
try {
result = division(a,b);
cout<<a<<" / "<<b<<" = "<<result<<endl;
}
catch (MyException e) {
cout<<e.show()<<endl;
}
cout<<endl;
cout<<"Enter two integers (EOF to end):";
}
return 0;
}
- 關鍵字throw可以帶任何類型的操作數,包括自定義類型(異常對象)
- 異常拋出後,最近的一個匹配的異常處理器捕獲該異常
- 如果沒有匹配的異常處理器,則系統調用terminate函數,terminate函數省卻地調用abort函數終止程序的執行
- 拋出異常時,throw語句生成異常對象的一個副本,異常處理器執行完畢後刪除該臨時對象
// ex17_2.cpp: 拋出多種類型異常的例子
#include <iostream>
using namespace std;
main()
{
int a, myint;
float myfloat;
double mydouble;
cout<<"Enter a integer (EOF to end):";
while (cin>>a){ //拋出不同類型的異常
try {
switch(a % 3) {
case 0: //輸入整數爲3的倍數時拋出整型異常
myint = a;
throw myint;
break;
case 1: //拋出float類型異常
myfloat = (float)a;
throw myfloat;
break;
case 2: //拋出double類型異常
mydouble = a;
throw mydouble;
break;
default:
break;
}
}
catch (int e) { //捕獲整型異常
cout<<"Integer Exception: "<<e<<endl;
}
catch (float e) { //捕獲浮點類型異常
cout<<"Float Exception: "<<e<<endl;
}
catch (double e) { //捕獲雙精度類型異常
cout<<"Double Exception: "<<e<<endl;
}
cout<<endl;
cout<<"Enter a integer (EOF to end):";
}
return 0;
}
- 異常只能在try塊中拋出,並由其後的符合類型的catch塊捕捉
- 在try塊外面拋出的異常將不會被捕捉到,系統會調用terminate函數終止程序的運行
- 發生異常後跳出拋出異常的程序塊,並且無法再返回到拋出點
- 異常可以在try塊中顯式拋出,也可以在其調用的函數中拋出
- 先在內層try塊後面的catch塊中尋找合適的異常處理器
- 找到則進行處理,異常不再往外傳播
- 如果找不到,則將該異常向外傳播,到外層try塊後面的catch塊中繼續尋找
- 如果異常傳播到最外層的try塊仍然找不到,則程序調用terminate函數
// ex17_3.cpp: 異常傳播的例子。
#include <iostream>
using namespace std;
int add(int a, int b)
{ //結果過大過小時都拋出異常
int res;
try
{
res = a + b;
if (res > 128) //拋出整型異常
throw res;
if (res<0) //拋出字符串異常
throw "Negative result!";
}
catch (int e) { //捕捉整型異常
cout<<"The result is too large :"<<e<<endl;
return -1;
}
return res;
}
int main()
{
int a, b, result;
cout<<"Enter two integers (EOF to end):";
while (cin>>a>>b){
try {
result = add(a, b);
if (result >= 0)
cout<<"The result is "<<result<<endl;
}
catch (...) { //捕捉傳播到外層的所有異常
cout<<"Unexpected exception."<<endl;
}
cout<<endl;
cout<<"Enter a integer (EOF to end):";
}
return 0;
}
- 異常處理器以關鍵字catch開始
- 異常處理器能帶一個參數(能捕捉的異常類型),參數名可選
- 有參數名時,可以在異常處理器內使用這個參數,該參數只是拋出的異常對象的一個副本catch (int e){…}
- 沒有參數名時,異常對象不從拋出點傳遞到異常處理器中catch (int){…}
- 程序按順序尋找匹配的異常處理器,拋出的異常將第一個類型符合的異常處理器捕獲
- 如果內層try塊後面沒有找到合適的異常處理器,該異常向外傳播,到外層try塊後面尋找
- 沒有被捕獲的異常將調用terminate函數,terminate函數默認調用abort終止程序的執行
- 可以使用set_terminate函數指定terminate函數將調用的函數
- 參數列表中只有一個省略號的異常處理器能捕捉所有類型的異常catch (...) {...}
- 要注意異常處理器的排列順序,可能會影響異常處理的結果
- 異常處理器的參數類型和拋出異常的類型完全相同
- .異常處理器的參數類型是拋出的異常對象的基類
- 異常處理器的參數是基類的指針或引用,拋出異常的類型是派生類的指針或引用
- 異常處理器的參數是void*類型的指針,拋出異常的類型是某一種類型的指針
- 異常處理器爲catch(…)
//file ex17_4.h
#include <iostream>
using namespace std;
//定義基類
class base {
public:
void show() {
cout<<"Base object."<<endl;
}
};
//定義派生類
class derived :public base {
public:
void show() {
cout<<"Derived object."<<endl;
}
};
// ex17_4.cpp: 拋出基類和派生類異常
#include "ex17_4.h"
main()
{
int no;
cout<<"Input a integer please:";
while(cin>>no)
{
try {
if ((no % 2) == 0) //拋出基類對象
throw base();
else //拋出派生類對象
throw derived();
}
catch(base b) {
cout<<"Exception:";
b.show();
}
catch(derived d) {
cout<<"Exception:";
d.show();
}
cout<<endl<<"Input a integer please:";
}
return 0;
}