当前位置: 首页 > news >正文

C++ 异常

文章目录

  • 1. 异常概念
  • 2. 异常的抛出与捕获
    • 2.1 异常抛出
    • 2.2 异常捕获
    • 2.3 异常抛出与捕获逻辑:栈展开
    • 2.4 异常抛出和捕获:特殊情况
      • 2.4.1 throw
      • 2.4.2 catch
  • 3. 自定义异常类型
    • 3.1 自定义异常类型示例
  • 4. 异常安全问题
    • 4.1 noexcept关键字和操作符

1. 异常概念

C中处理错误的方式一般通过函数的异常返回值,设置错误码errno的形式实现。这种方式错误码提供的信息有限,并不能很好显示异常问题。

因此,C++中引入全新的一套错误处理机制:异常。

异常的处理中,涉及异常的抛出与捕获,实际抛出的异常本质是一个具体对象,类型是任意的,捕获时需要注意类型匹配,才可正确捕获。

2. 异常的抛出与捕获

2.1 异常抛出

异常抛出使用trythrow的结合。

try
{throw exception;
}

try后必须接{}所含的语句块,throw异常的行为,必须在try中进行,只有在try中抛出的异常,才会被捕获。

2.2 异常捕获

异常捕获使用catch

catch(type exception)
{}

catch后,()内是用于接收所抛出异常的对象,{}catch处理异常的语句块,与try相同,必须采用语句块形式。

2.3 异常抛出与捕获逻辑:栈展开

实际程序中,会涉及到函数的多层调用,即函数中调用函数

如果在一个函数栈帧较深的函数中,抛出一个异常,且每层函数调用中,均有相应的捕获逻辑,会有什么行为呢?

异常的捕获:优先匹配最近的且类型能够匹配的catch。
在这里插入图片描述
在整个catch的匹配过程中,对于调用链上不匹配的catch所对应的函数栈帧会直接退出,剩余的代码将不会再被执行。 直至跳转到匹配的catch,就继续维护相应的函数栈帧,从其catch开始,继续执行之后的代码,直至函数返回。

明白整个异常抛出与捕获过程,自然会产生一个问题?抛出异常,往往是抛出一个函数栈内的对象,如果在当前函数栈内不匹配,回退到别的函数栈中匹配,那么此时这个对象不就被销毁了吗?

实际上,真正捕获的异常对象,并不是最初抛出的某个函数栈内的对象,编译器处理异常时,实际会构造一个新的异常对象(自定义类型,通常都是移动构造,转移资源),这个异常对象的生命周期持续整个catch匹配的过程,直至执行完所匹配catch的相关语句后,才被销毁。

这整个异常抛出和捕获的逻辑,其中函数调用链逐渐回退,直至匹配到相应catch,就被称为栈展开。

2.4 异常抛出和捕获:特殊情况

2.4.1 throw

如果一个异常抛出,直到回到main函数中,都没有匹配的catch,会怎样?
这种情况是非法的,一个异常如果最终未被捕获,那么当前进程就会被异常终止。
同时,这也就说明一点,抛出异常的行为必须在try中进行,只有在try中抛异常方具备被捕获的必要条件。

异常捕获,除了指明类型外,还可以进行任意类型捕获,即任意类型的异常到来,都可匹配。

2.4.2 catch

catch(...)
{
}

上述写法便是任意类型捕获。

此外,已经被catch捕获的异常,可被再次抛出吗?可以在catch中抛出捕获的异常,且在catch中抛出的异常,与正常抛出异常行为无差别。

try
{throw exception;
}
catch(const type& exception)
{throw;//可以直接写throw,默认将捕获到的异常抛出
}

3. 自定义异常类型

实际工程中,一般都是专门的异常类型对象,这个异常类型可以是自定义的,也可是库中提供的。
比起抛出其它类型,专门的异常类型中包含更多的相关错误信息,能帮助程序员更好地进行错误的了解和处理。

在这里插入图片描述
上图中的exception表示C++库中专门实现的异常类所在的头文件,包含了相关异常类型的声明和实现,本质是一个.hpp文件。

异常类型一般也会设计父类与子类的继承体系,实际抛出异常时,可能抛出一个父类异常,也可能抛出子类异常,此时有两种捕获方案:写多个catch,对父类和子类异常分别进行捕获;只对父类异常进行捕获,本质是利用多态机制,实际的子类对象可以给父类类型的引用,实际调用成员函数时,通过运行时多态,执行相应的函数。
实时使用时,一般不写多个catch捕获,而是统一使用父类类型的引用去统一捕获父类或子类的异常对象。

3.1 自定义异常类型示例

下面展示一个自行设计的异常类型,并完成抛出与捕获:

class Exception
{
public:Exception(int id,const std::string& str):_id(id),_str(str){ }virtual std::string what(){std::string str = _str + ' ' + std::to_string(_id);return std::move(str);}virtual ~Exception(){ }protected:int _id;//出错编号std::string _str;//出错信息
};class DevException :public Exception
{
public:DevException(int id, const std::string& str, int client_num):Exception(id,str),_client_num(client_num){}std::string what() override {std::string str = _str + ' ' + std::to_string(_id) + ' ' + std::to_string(_client_num);return std::move(str);}~DevException() override{ }private:int _client_num;//出错员工号
};int main()
{try{throw DevException(1, "测试继承异常中的抛出与捕获",100);}catch (Exception& e){std::cout << e.what() << std::endl;}return 0;
}

异常自定义类型中,通常都会提供一个what接口,返回一个string对象,用于将相关异常错误信息打印输出。

4. 异常安全问题

C++11中,异常引入为错误处理带来极大遍历,但同时也引入异常安全问题。

最常见的异常安全问题就是内存泄漏。

例如,在存在多条new语句的时候,由于每条new语句都存在抛出异常的可能,而由于异常抛出与捕获的栈展开逻辑,如果不再每条new语句后都添加catch逻辑,最终很可能因为栈展开的跳转逻辑,引发内存泄漏(通常对于第一个new之后的new)。而如果每条new语句后,都添加catch,代码又会显得非常冗余。

为了解决异常安全问题,尤其是异常安全中的内存泄漏,C++中又引入智能指针,以RAII风格进行资源申请与释放。

4.1 noexcept关键字和操作符

noexcept是一个关键字和编译期操作符。

关键字时,用于声明一个函数不能抛出异常。如果一个函数可能抛出异常,声明为noexcept,通常编译不会报错,但是如果实际运行时,抛出异常,当前进程会被异常终止。

操作符时,用于检测一个表达式是否可能抛出异常,可能抛出异常,返回flase;否则,返回true。
比如通过noexcept() 检测一个函数调用表达式是否会抛出异常,不过这个检测实际会受函数noexcept声明的影响——一个函数可能抛出异常,但声明为noexcept,进行判断,仍会返回true。


文章转载自:

http://BXw0wJUV.kngqd.cn
http://xhZswEoq.kngqd.cn
http://u0HsvRcf.kngqd.cn
http://0Otwwyv6.kngqd.cn
http://6i6BIObf.kngqd.cn
http://6tpC9dn5.kngqd.cn
http://zv05JGVm.kngqd.cn
http://33vLICic.kngqd.cn
http://ViC8CQQz.kngqd.cn
http://8KvE8Xhx.kngqd.cn
http://qXgB0Oxn.kngqd.cn
http://pt9DAcV7.kngqd.cn
http://oOFXOUVt.kngqd.cn
http://q0daYVAN.kngqd.cn
http://RxaiqT5U.kngqd.cn
http://OU7YpNlS.kngqd.cn
http://u7uwCctX.kngqd.cn
http://LbXIM9zE.kngqd.cn
http://9dICjkgw.kngqd.cn
http://qnd2VHBu.kngqd.cn
http://HWJL9JIk.kngqd.cn
http://dNHUxHT1.kngqd.cn
http://KyZzSbGg.kngqd.cn
http://x2a4yE19.kngqd.cn
http://64br2DaB.kngqd.cn
http://IDGHKt0F.kngqd.cn
http://Royp6h7t.kngqd.cn
http://AqdLHdIM.kngqd.cn
http://TkngtmGk.kngqd.cn
http://kR9BAFIx.kngqd.cn
http://www.dtcms.com/a/382555.html

相关文章:

  • One-hot encoding|独热编码
  • AI论文速读 | VisionTS++:基于持续预训练视觉主干网络的跨模态时间序列基础模型
  • 如何学习VBA_3.3.9:利用“搭积木”思想,快速有效地完成你的代码
  • 《使用深度学习统一时间相位展开框架》论文总结
  • Windows下使用PerfMon进行性能监控并记录日志
  • 微信小程序开发教程(十二)
  • 【攻防实战】记一次攻防实战全流程
  • 【编号520】全国4500多个地震灾害点位数据(2021.2-2025.8)
  • 牛客网习题题解(持续更新中...)
  • 2025.8.20--python基础温习
  • PCIE地址空间介绍
  • (三)BLE协议栈协议分层架构设计详解--图文结合通俗易懂
  • 主板硬件研发基础--DP/DP++
  • 287. 寻找重复数
  • 《从像素到认知:用 Keras 构建图像分类 CNN 的实战指南》
  • 深入探索 Python 元组:从基础到高级应用
  • Java 黑马程序员学习笔记(进阶篇5)
  • DENOISING DIFFUSION IMPLICIT MODELS
  • Gradle 安装与配置 环境配置 仓库管理 项目介绍 优缺点介绍
  • Replit CEO演讲:软件开发的未来与AI代理革命
  • LeetCode 3541.找到频率最高的元音和辅音:计数(位运算)
  • 使用Python创建本地Http服务实现与外部系统数据对接
  • Redis 线上问题排查简版手册
  • python学习之基本库/第三方库的认识和学习
  • 深度解析电动汽车绝缘材料的性能测试标准与解决方案
  • 通讯工程师专业实务-数据库、软件开发、云计算
  • 栈(Java)
  • StarRocks and Doris
  • Python进阶教程:随机数、正则表达式与异常处理
  • 【面试题】大模型高频面试题