18异常处理
程序执行中需要处理异常:
1.动态分配空间时可能不会成功
2.打开文件可能会失败
3.除法运算时分母可能为0
4.整数相乘可能溢出
5.指针可能越界等情况
异常处理方法一:
1.使用选择语句(if…else…)
2.判断异常情况,即时处理
3.正常程序流程和异常处理语句混在一起
4.程序员往往无法专注于正常流程编程
异常处理方法二:
1.使用C++异常处理机制
2.判断异常情况,发现异常后抛出异常
3.正常程序流程和异常处理模块分开
4.程序员可以专注于正常流程编程,异常处理模块稍候编写
C++异常处理机制
1.程序在产生错误后抛出异常
2.异常处理模块捕获并处理异常
3.异常处理机制一般无法使程序恢复正常执行
4.可以为程序提供有序的整理操作(如析构对象)
关键字try:出错时产生异常的代码放在try块中
关键字throw:throw语句可以抛出任意类型的异常,包括自定义类型
关键字catch:catch块(异常处理器)捕捉和处理异常
注意:
1.一个异常处理器一般只捕捉一种类型的异常
2.try块抛出异常后,程序控制离开try块
3.抛出异常后,程序在try块后面的catch块中逐个搜索合适的异常处理器
4.如果try块没有异常抛出,则程序跳过所有catch块
5.抛出异常之后,程序控制无法返回到抛出点
6.try块可以直接或间接抛出异常
示例;
除数为零的异常处理
#include <iostream.h>
#include <string.h>
//定义异常类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;
}
main()
{int a, b;
double result;
cout<<"Enter two integers (EOF: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:end):";
}
return 0;
}
异常的抛出和传播(一)
1.关键字throw可以带任何类型的操作数(任意表达式),包括自定义类型(如异常对象)
2.异常抛出后,最近匹配的异常处理器捕获该异常
3.如果没有匹配的异常处理器,则系统调用terminate函数,terminate函数默认调用abort函数终止程序的执行
4.抛出异常时,throw语句生成异常数据的一个副本,异常处理器执行完毕后删除
示例:抛出各类异常,并捕捉它们
#include <iostream.h>
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;
}
异常的抛出和传播(二)
1.在try块中直接或间接抛出的异常,由其后匹配的catch块捕捉
2.在try块外面抛出的异常将不会被捕捉到,系统会调用terminate函数终止程序的运行
3.发生异常后跳出抛出异常的程序块,并且无法再返回到抛出点
4.异常可以在try块中显式抛出,也可以在其调用的函数中抛出
异常的抛出和传播(三)
try块可以嵌套
内层try块抛出异常的传播顺序
(1)先在内层try块后面的catch块中寻找合适的异常处理器
(2)找到则进行处理,异常不再往外传播
(3)如果找不到,则将该异常向外传播,到外层try块后面的catch块中继续寻找
(4)如果异常传播到最外层的try块仍然找不到,则程序调用terminate函数
示例:异常传播的例子
#include <iostream.h>
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; }
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;
}
异常的捕获和处理
1.异常处理器以关键字catch开始
2.异常处理器能带一个参数(能捕捉的异常类型),参数名可选
3.有参数名时,可以在异常处理器内使用这个参数,该参数只是抛出的异常对象的一个副本 catch (int e){…}
4.没有参数名时,异常对象不从抛出点传递到异常处理器中 catch (int){…}
5.程序按顺序寻找匹配的异常处理器,抛出的异常被第一个类型符合的异常处理器捕获
6.如果内层try块后面没有找到合适的异常处理器,该异常向外传播,到外层try块后面寻找
7.没有被捕获的异常将调用terminate函数,terminate函数默认调用abort函数终止程序执行
8.可以使用set_terminate函数指定terminate函数将调用的函数
9.参数列表中只有省略号的异常处理器能捕捉所有类型的异常catch (...) {语句序列}
10异常处理器的排列顺序,可能会影响异常处理的结果
Visual C++: try后必须跟至少一个catch ; set_terminate有问题
满足下面条件之一时,异常被捕捉:
1.异常处理器的参数类型和抛出的异常数据的类型完全相同
2.异常处理器的参数类型是抛出的异常对象的基类
3.异常处理器的参数是基类的指针或引用,抛出的异常数据类型是派生类的指针或引用
4.异常处理器的参数是void*类型的指针,抛出的异常数据类型是某一种类型的指针
5.异常处理器为catch(…)
示例:异常捕获的例子
#include <iostream.h>
//定义基类
class base
{public:
void show()
{cout<<"Base object."<<endl;}
};
//定义派生类
class derived :public base
{public:
void show()
{cout<<"Derived object."<<endl;}
};
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;
}