概述
程序执行中需要处理异常
- 动态分配空间时可能不会成功
- 打开文件可能会失败
- 除法运算时分母可能为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 (...) {...}
- 要注意异常处理器的排列顺序,可能会影响异常处理的结果
满足下面条件之一时,异常被捕捉
- 异常处理器的参数类型和抛出异常的类型完全相同
- .异常处理器的参数类型是抛出的异常对象的基类
- 异常处理器的参数是基类的指针或引用,抛出异常的类型是派生类的指针或引用
- 异常处理器的参数是void*类型的指针,抛出异常的类型是某一种类型的指针
- 异常处理器为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:
最后
以上就是无聊八宝粥为你收集整理的C++学习笔记之异常的全部内容,希望文章能够帮你解决C++学习笔记之异常所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复