C++异常(exception)

C++异常(exception)

​ 所谓的异常就是不正常,指在程序运行的过程中,由于系统条件、操作不当等原因所引起的程序运行错误
​ 常见的异常:除零错误、指针访问受保护的空间、数组越界、内存分配失败、打开的文件不错在…

在C++中,新增加了一种错误(异常)处理方式
错误一般分为两种:编译时错误、运行时错误

编译时错误:在程序编译阶段就能够发现的错误
运行时错误:编译能够通过,在运行的时候会有一些错误产生

为了提高系统的健壮性,加强容错能力,在编程过程中,这些错误(异常)必须要处理,防止系统崩溃

我们前面写过的代码都是在哪里发现问题就在哪里解决问题,这种处理方式就是分散式处理
将错误(异常)分散在各处进行处理,不利于代码的维护,尤其是对于不同的地方发生相同的错误(异常),
或者多个地方的错误(异常)处理方式一样,都需要编写相同的代码来进行处理,这也是一种代码的冗余和重复

如果能够在发生各种异常的时候,让程序都自动执行同一个地方的代码,
这个地方的代码能够对所有的异常进行集中的处理,则程序会更容易编写和维护

所以,在C++中引入了异常处理的机制:

基本思想:

​ 函数A在执行的过程中,如果产生了异常,在该函数中不处理异常,而是将该异常抛出,即该函数只"抛出一个异常"
​ 该异常会抛给A的调用者B

​ 抛出异常以后,会导致函数A终止执行,类似于return,
​ 在这种情况下,函数B可以对A的异常进行处理,也可以当作没看见(不处理)

​ 如果函数B对异常置之不理,这个异常就会继续抛给函数B的调用者C,C有两种处理方式:对异常进行处理或者置之不理…

​ 如果一层层的函数都不处理异常,异常最终会抛给最外层的main函数,
​ main函数应该要处理这个异常,如果不处理,那么这个异常就会抛给main函数的调用者(操作系统)
​ 操作系统就会立即终止(kill)main函数

C++处理异常的进制由三部分组成:

​ 1.抛出异常(throw)(类似于return)
​ 2.捕获异常(try-catch)(类似于if-switch)
​ 3.处理异常

因此,C++引入了throw/try/catch关键字用来处理异常

通常将一组可能会产生异常的代码放到try所保护的代码块内,
如果产生了异常,就由try后面的catch结构来接受异常并处理异常

如果try内没有产生异常,则catch不会被执行,代码正常运行

异常处理结构:
try
{
    //可能会出现异常的代码
}catch(类型名1 [形参名])         //[]括起来表示形参可有可无,如果需要使用异常值,就可以设置形参,如果不需要就可以不设置
{
    //异常处理方式1
}catch(类型名2 [形参名])
{
    //异常处理方式2
}catch(类型名3 [形参名])
{
    //异常处理方式3
}
...
catch(...)
{
    //表示不确定的异常类型,这里可以捕获其他类型的异常
}

异常处理机制,能够实现发现问题和解决问题分离,是一种思想:
在异常处理过程中,由函数内部来抛出问题(异常值、对象),问题会抛给调用该函数的调用者,
通过抛出的这个问题,实际上完成了两个部分的通信,通信的内容为:出现了什么问题,怎么解决!

抛出的异常值可以是任意类型,包括类类型
在捕获异常值时,catch是通过异常值的类型来进行匹配,所以如果不需要保存(接收)异常值,可以省略形参
异常值在传递到catch前,都会创建一个"临时对象",类似于return

C++标准库中定义了一组异常类型,用于报告标准库中的函数常见的问题,
这些异常类型可以直接在程序中使用(已经定义好了)

常见的异常类型有以下:

​ exception (异常,是所有异常类的基类)

​ runtime_error 运行时异常,没有默认构造函数,必须要提供参数
​ out_of_range 范围越界(数组越界),没有默认构造函数,必须要提供参数
​ bad_alloc 分配动态内存失败,只有默认构造函数,不需要提供参数
​ 当new失败的时候,会自动抛出该异常
​ …

​ 有的异常类型的对象在构造的时候需要提供参数(解释性的字符串),有的不需要提供参数
​ 在所有的异常类中只有成员函数:what,该函数没有参数
​ 作用是返回一个C语言风格的字符串,用来提供异常的信息

如:
抛出异常:
throw runtime_error(“运行时错误!”);
throw out_of_range(“范围越界!”);
throw bad_alloc();

​ 捕获异常:
​ catch(runtime_error &arg)
​ {
​ cout << “捕获到一个runtime_error类型的异常值” << endl;
​ cout << arg.what() << endl;
​ }
​ catch(out_of_range &arg)
​ {
​ cout << “捕获到一个out_of_range类型的异常值” << endl;
​ cout << arg.what() << endl;
​ }
​ catch(bad_alloc &arg)
​ {
​ cout << “捕获到一个bad_alloc类型的异常值” << endl;
​ cout << arg.what() << endl; //会打印std::bad_alloc
​ }

注意:

​ 构造函数中可以抛出异常!!!
​ 普通的成员函数中也可以抛出异常

​ 析构函数中不能抛出异常!!!
​ 析构函数默认是 noexcept 的

​ 如果一个函数绝对不会抛出异常,可以在函数的参数列表后面加 noexcept 进行说明
​ noexcept是一种承诺,说明该函数不会抛出异常

​ 如果一个函数用 noexcept 说明了,但是在函数内部又抛出了异常,
​ 那么在编译时会报警告
​ 一旦调用该函数,运行时系统会直接终止该程序!!!

异常测试代码

#include <iostream>
using namespace std;

class Test
{
};

//小孩独自去上学
void gotoSchool()
{
    int step = 0;   //上学的步骤
    bool isOk = true;   //小孩的状况
    //经过第一关
    cout << "经过女儿国" << endl;
    if (!isOk)
    {
        cout << "被女儿国国王抓走了" << endl;
        throw "被女儿国国王抓走了";
    }
    //经过第二关
    cout << "路过龙虎山" << endl;
    if (!isOk)
    {
        cout << "被老虎吃了" << endl;
        throw 9999;
    }
    //经过第三关
    cout << "通过流沙河" << endl;
    if (!isOk)
    {
        cout << "成了沙和尚的小点心" << endl;
        throw 'g';
    }
    //经过第四关
    cout << "通过红绿灯路口" << endl;
    if (!isOk)
    {
        cout << "被车撞了" << endl;
        throw 3.14f;
    }
    //可以抛出任意类型的数据,包括自定义类型
    cout << "Test" << endl;
    if (!isOk)
    {
        cout << "抛出自定义类型的数据" << endl;
        Test ts;
        throw ts;
    }
    //抛出标准库中的异常类型的对象
    cout << "运行时错误" << endl;
    if (!isOk)
    {
        cout << "抛出标准库中的运行时错误的对象" << endl;
        throw runtime_error("运行时错误!");
    }
    cout << "范围越界" << endl;
    if (!isOk)
    {
        cout << "抛出标准库中的范围越界的对象" << endl;
        int array[3];
        int i = 4;
        if (i > 2)
        {
            throw out_of_range("范围越界!");
        }
    }
    cout << "分配内存失败" << endl;
    isOk = false;
    if (!isOk)
    {
        cout << "抛出标准库中的分配内存失败的对象" << endl;
        throw bad_alloc();
    }
    cout << "成功抵达学校!" << endl;
}


int main()
{
    cout << "清早起床了" << endl;
    cout << "送小孩去上学" << endl;
    cout << "朋友喊打麻将,三缺一" << endl;
    cout << "让小孩自己去上学" << endl;
    try
    {
        gotoSchool();
        cout << "呦呦呦,煎饼果子来一套!" << endl;
    }
    catch(int arg)
    {
        cout << "捕获到一个int型的异常值" << endl;
        cout << arg << endl;
    }
    catch(char arg)
    {
        cout << "捕获到一个char型的异常值" << endl;
        cout << arg << endl;
    }
    catch(const char *arg)
    {
        cout << "捕获到一个字符串类型的异常值" << endl;
        cout << arg << endl;
    }
    catch(float arg)
    {
        cout << "捕获到一个float型的异常值" << endl;
        cout << arg << endl;
    }
    catch(Test arg)
    {
        cout << "捕获到一个Test类型的异常值" << endl;
    }
    catch(runtime_error &arg)
    {
        cout << "捕获到一个runtime_error类型的异常值" << endl;
        cout << arg.what() << endl;
    }
    catch(out_of_range &arg)
    {
        cout << "捕获到一个out_of_range类型的异常值" << endl;
        cout << arg.what() << endl;
    }
    catch(bad_alloc &arg)
    {
        cout << "捕获到一个bad_alloc类型的异常值" << endl;
        cout << arg.what() << endl;
    }
    catch(...)
    {
        cout << "捕获到一个未知类型的异常值" << endl;
    }
    cout << "结束" << endl;
}
  • 14
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值