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:






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值