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类异常,导致异常没有被捕获。程序运行结果如下:

相关推荐
2401_8543910812 分钟前
城镇住房保障:SpringBoot系统功能概览
java·spring boot·后端
hummhumm13 分钟前
Oracle 第29章:Oracle数据库未来展望
java·开发语言·数据库·python·sql·oracle·database
wainyz22 分钟前
Java NIO操作
java·开发语言·nio
工业3D_大熊28 分钟前
【虚拟仿真】CEETRON SDK在船舶流体与结构仿真中的应用解读
java·python·科技·信息可视化·c#·制造·虚拟现实
喵叔哟31 分钟前
重构代码之用委托替代继承
开发语言·重构
lzb_kkk37 分钟前
【JavaEE】JUC的常见类
java·开发语言·java-ee
SEEONTIME37 分钟前
python-24-一篇文章彻底掌握Python HTTP库Requests
开发语言·python·http·http库requests
Zfox_37 分钟前
【Linux】进程信号全攻略(二)
linux·运维·c语言·c++
起名字真南1 小时前
【OJ题解】C++实现字符串大数相乘:无BigInteger库的字符串乘积解决方案
开发语言·c++·leetcode
少年负剑去1 小时前
第十五届蓝桥杯C/C++B组题解——数字接龙
c语言·c++·蓝桥杯