C++學習筆記之異常

程序執行中需要處理異常
  • 動態分配空間時可能不會成功
  • 打開文件可能會失敗
  • 除法運算時分母可能爲0
  • 整數相乘可能溢出
  • 指針可能越界
  • ……
異常處理方法一
  • 使用選擇語句(if…else…)
  • 判斷異常情況,即時處理
  • 正常程序流程和異常處理語句混在一起
  • 程序員往往無法專注於正常流程編程
異常處理方法二
  • 使用C++異常處理機制
  • 判斷異常情況,發現異常後拋出異常
  • 正常程序流程和異常處理模塊分開
  • 程序員可以專注於正常流程編程,異常處理模塊稍候編寫
C++異常處理機制
  • 程序在產生錯誤後拋出異常
  • 異常處理模塊捕獲並處理異常
  • 異常處理機制一般無法使程序恢復正常執行
  • 可以爲程序提供有序的整理操作
異常處理基礎
  • 關鍵字try:出錯時產生異常的代碼放在try塊中
  • 關鍵字throw:throw語句可以拋出任意類型的異常,包括自定義類型
  • 關鍵字catch:catch塊(異常處理器)捕捉和處理異常
  • 一個異常處理器一般只捕捉一種類型的異常
  • try塊拋出異常後,程序控制離開try塊
  • 拋出異常後,程序在try塊後面的catch塊中逐個搜索合適的異常處理器
  • 如果try塊沒有異常拋出,則程序跳過所有catch塊
  • 拋出異常之後,程序控制無法返回到拋出點
  • try塊可以直接或間接拋出異常
例子1:除數爲零的異常處理
// 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;
}
程序運行結果
Enter two integers (EOF to end):12 7
12 / 7 = 1.71429
 
Enter two integers (EOF to end):2 0
error: divided by zero!
 
Enter two integers (EOF to end):34 5
34 / 5 = 6.8
 
Enter two integers (EOF to end):

異常的拋出和傳播
  • 關鍵字throw可以帶任何類型的操作數,包括自定義類型(異常對象)
  • 異常拋出後,最近的一個匹配的異常處理器捕獲該異常
  • 如果沒有匹配的異常處理器,則系統調用terminate函數,terminate函數省卻地調用abort函數終止程序的執行
  • 拋出異常時,throw語句生成異常對象的一個副本,異常處理器執行完畢後刪除該臨時對象
例子2:編寫程序拋出各類異常,並捕捉它們
// 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;
} 
程序運行結果
Enter a integer (EOF to end):10
Float Exception: 10
 
Enter a integer (EOF to end):11
Double Exception: 11
 
Enter a integer (EOF to end):12
Integer Exception: 12
 
Enter a integer (EOF to end):13
Float Exception: 13
 
Enter a integer (EOF to end):

  • 異常只能在try塊中拋出,並由其後的符合類型的catch塊捕捉
  • 在try塊外面拋出的異常將不會被捕捉到,系統會調用terminate函數終止程序的運行
  • 發生異常後跳出拋出異常的程序塊,並且無法再返回到拋出點
  • 異常可以在try塊中顯式拋出,也可以在其調用的函數中拋出
try塊可以嵌套
內層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;
} 

程序運行結果
Enter two integers (EOF to end):12 66
The result is 78
 
Enter a integer (EOF to end):23 456
The result is too large :479
 
Enter a integer (EOF to end):-123 34
Unexpected exception found.
 
Enter a integer (EOF to end):

異常的捕獲和處理
  • 異常處理器以關鍵字catch開始
  • 異常處理器能帶一個參數(能捕捉的異常類型),參數名可選
  • 有參數名時,可以在異常處理器內使用這個參數,該參數只是拋出的異常對象的一個副本catch (int e){…}
  • 沒有參數名時,異常對象不從拋出點傳遞到異常處理器中catch (int){…}
  • 程序按順序尋找匹配的異常處理器,拋出的異常將第一個類型符合的異常處理器捕獲
  • 如果內層try塊後面沒有找到合適的異常處理器,該異常向外傳播,到外層try塊後面尋找
  • 沒有被捕獲的異常將調用terminate函數,terminate函數默認調用abort終止程序的執行
  • 可以使用set_terminate函數指定terminate函數將調用的函數
  • 參數列表中只有一個省略號的異常處理器能捕捉所有類型的異常catch (...) {...}
  • 要注意異常處理器的排列順序,可能會影響異常處理的結果

滿足下麪條件之一時,異常被捕捉
  1. 異常處理器的參數類型和拋出異常的類型完全相同
  2. .異常處理器的參數類型是拋出的異常對象的基類
  3. 異常處理器的參數是基類的指針或引用,拋出異常的類型是派生類的指針或引用
  4. 異常處理器的參數是void*類型的指針,拋出異常的類型是某一種類型的指針
  5. 異常處理器爲catch(…)
例子4:異常捕獲的例子
//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;
}

程序運行結果
Input a integer please:1
Exception:Base object.
 
Input a integer please:2
Exception:Base object.
 
Input a integer please:3
Exception:Base object.
 
Input a integer please:4
Exception:Base object.
 
Input a integer please:






發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章