C++---异常


一.异常的介绍

当我们编写比较复杂的,个人难以管理的系统时,异常和命名空间最为有用。

异常在工作中经常使用,异常就是在程序运行中发生的难以预料的、不正常的事件而导致偏离正常流程的现象。发生异常将导致正常流程不能进行,就需要对异常进行处理。

异常使得我们能将问题的检测与解决过程分离开来。程序的一部分负责检测问题的出现,然后解决该问题的任务传递给程序的另一部分。检测环节无须知道问题处理模块的细节。

如下,对于除0的处理

cpp 复制代码
#include <iostream>
using namespace std;

double fun(double a, double b)//除法函数
{
    if (b == 0)//除数为0,抛出异常
    {
        throw b;
    } 
    return a / b; //否则返回两个数的商
}
int main()
{
    double res;
    try //可能出现异常
        {
            res = fun(4, 5);
            cout << 4 << "/" << 5 << " = " << res << endl;
            res = fun(6, 0);//出现异常,函数内部会抛出异常
            cout << 6 << "/" << 0 << " = " << res << endl;//这一句没有执行
        }
    catch (double a) //捕获并处理异常
        {
            cout << "a=" << a << endl;
            cerr << "出现除0错误,程序中止了,请检查!\n";
            exit(1); //异常退出程序
        }
    return 0;
}

异常的抛出和处理主要使用了:throw语句和try-catch语句。


throw语句

如果检测到异常,则抛出异常。

格式为:

cpp 复制代码
throw 表达式;

表达式的类型作为异常事件的类型,表达式的类型和值将传给捕获该异常的程序。表达式可以是一个基本类型,也可以是一个对象。这个表达式的类型可以和函数的返回值类型不同。

当执行一个throw时,跟在thorw后面的语句将不再被执行。程序的控制权从throw转移到与之匹配的catch模块。

try-catch语句

捕获处理异常的语句是try-catch语句。

该语句格式如下:

cpp 复制代码
try{ 可能引发异常的语句序列 }           //受保护代码
catch(异常类型1 异常变量1){ 处理代码1}  //异常处理1
catch(异常类型2 异常变量2){ 处理代码2} //异常处理2
...
catch(异常类型n 异常变量n){ 处理代码n} //异常处理n
catch(...) { 处理代码n+1}            //异常处理n+1

其中,关键字try包括的语句称为try子句 。这个语句块中的代码被称为受保护代码 ,可以包含多条语句。受保护代码描述正常的执行流程,但这些语句的执行可能引发异常。如果没有发生异常,try-catch 语句就正常结束,所有的catch都不执行。如果引发了某种类型的异常,就按 catch 子句顺序逐个匹配异常类型,捕获并处理该异常。

**异常是按其类型进行捕获的。**一个catch子句捕获一类异常。异常类型及变量指明要捕获的异常的类型,及接收异常的值。例如,catch(double a),要捕获的异常类型为double,如果真的捕获到该类异常,那么变量a就有这个异常的值,这个值就是前面用throw语句抛出的。

有一种特殊的catch子句,就是catch(...),它的含义是捕获所有异常。在多个 catch 子句中,它应放在最后。

异常处理步骤

在执行try子句中的受保护代码时,如果引发一个异常,系统就到catch子句中寻找处理该异常类型的入口。这个寻找过程称为异常类型匹配,它按如下步骤进行。

(1)由throw语句引发异常事件之后,系统依次检查catch子句以寻找相匹配的处理异常事件入口。如果某个 catch 子句的异常类型与异常事件类型相一致,该异常就被捕获,然后执行该子句的异常处理代码。如果有多个 catch 子句的异常类型相匹配,只执行最前面第一个匹配的异常处理代码。

(2)若没有找到任何相匹配的 catch 子句,该异常就被传递到外层作用域。如果外层作用域是函数,就传递到函数的调用方。

一个异常的生命期从被throw抛出来,然后被某个catch子句捕获,其生命期就结束了。一个异常从抛出来到被捕获,可能穿越多层作用域或函数调用。如果到main函数都未被捕获,将导致程序终止。

例如下面的程序

cpp 复制代码
#include <iostream>
#include <string>
using namespace std;

class DivdeByZeroException //定义除0异常类
{
string message; //记录异常信息
public:
DivdeByZeroException() :message("除0错误!\n") {}
const string& what() { return message; }
};

double fun(int a, int b) //除法函数
{
    if (b == 0) //抛出异常
    {
        throw DivdeByZeroException{}; //异常类型为DivdeByZeroException
    }
    return(double)a / b; //返回两个数的商
}
int main()
{
    int n1, n2;
    double res;
    cout << "请输入两个整数 : ";
    while (cin >> n1 >> n2)
        {
            try //受保护代码
                {
                    res = fun(n1, n2);
                    cout << n1 << "/" << n2 << " = " << res << endl;
                }
            catch (DivdeByZeroException ex) //捕获并处理异常
                {
                    cout << ex.what();
                }
            cout << "请输入两个整数 : ";
        }
    return 0;
}

未捕获异常的处理

如果一个异常没有被catch捕获,这种情况就叫未捕获异常。如果一个异常没有被捕获,则系统将自动调用abort()函数来终止程序执行。

cpp 复制代码
class A {};
class B {};

void f()
{
    int err = 1;
    if (err)
    {
        cout << " 抛出异常 B" << endl;
        throw B(); //抛出异常,类型为B
    }
}
int main()
{
    try //保护代码
        {
            f();//函数内部抛出异常
        }
    catch (A) //捕获A类型异常
        {
            cout << " 捕获异常类型 A" << endl;;
            exit(1); 
        }
    cout << " 再见" << endl;;
    return 0;
}

上述代码,抛出的是B类异常,但捕获的是A类异常,导致异常没有被捕获。程序运行结果如下:

相关推荐
曙曙学编程几秒前
初级数据结构——树
android·java·数据结构
小技与小术4 分钟前
数据结构之树与二叉树
开发语言·数据结构·python
Beau_Will4 分钟前
数据结构-树状数组专题(1)
数据结构·c++·算法
BestandW1shEs6 分钟前
彻底理解消息队列的作用及如何选择
java·kafka·rabbitmq·rocketmq
爱吃烤鸡翅的酸菜鱼9 分钟前
Java算法OJ(8)随机选择算法
java·数据结构·算法·排序算法
码蜂窝编程官方12 分钟前
【含开题报告+文档+PPT+源码】基于SpringBoot+Vue的虎鲸旅游攻略网的设计与实现
java·vue.js·spring boot·后端·spring·旅游
hccee25 分钟前
C# IO文件操作
开发语言·c#
Viktor_Ye28 分钟前
高效集成易快报与金蝶应付单的方案
java·前端·数据库
hummhumm30 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
一二小选手35 分钟前
【Maven】IDEA创建Maven项目 Maven配置
java·maven