【c++】异常处理

embedded/2025/3/26 6:12:55/

🌟🌟作者主页:ephemerals__
🌟🌟所属专栏:C++

目录

前言

一、什么是异常

二、异常的使用

三、catch语句匹配的特例

四、异常的重新抛出

五、异常安全问题

六、异常规范

七、标准库异常体系

总结


前言

        在C++项目开发中,异常处理(Exception Handling)是确保程序稳定性和可靠性的重要机制。无论是文件读取失败、内存分配错误,还是网络请求超时,程序运行过程中总会遇到各种意外情况。如果没有合理的异常处理,程序可能会崩溃,导致数据丢失或系统故障,严重影响用户体验。通过C++的异常处理机制,开发者可以在错误发生时优雅地捕获并处理异常,避免程序意外终止,同时提供必要的错误信息以便调试和优化。本篇文章,作者将介绍C++的异常处理基础、常见用法及最佳实践,帮助大家构建更健壮的C++应用程序。

一、什么是异常

        当程序在运行过程中出现问题,没有达到理想效果时,异常可以帮助我们对这些问题进行通信并做出相应的处理,将问题的检测与解决问题的过程分离开,更好地应对大型项目可能出现的各种情况。

相比于C语言处理错误的方式--返回错误码,我们在c++中,遇到异常时可以抛出一个对象,该对象所携带的错误信息可以更加全面。

二、异常的使用

        当程序没有达到我们想要的效果时,我们可以使用throw抛出一个对象来引发一个异常,该对象的类型决定了异常捕获的位置。

示例代码:

#include <iostream>
using namespace std;void func()
{try{int x = 0;cout << "input x:";cin >> x;if (x == 0)//假设x为0视为出现异常{throw "输入错误";}cout << "正常" << endl;}catch (const char* s){cout << s << endl;}
}int main()
{func();func();return 0;
}

运行结果:

当我们使用throw抛出异常时,程序会检查throw语句是否在try语句块中,如果在,则去寻找对应的catch语句(参数与对象类型相同),然后停止执行throw后的语句,转而执行catch语句块中的语句,然后执行后续代码。

注:如果throw语句未使用在try语句块中,程序会遇到“未捕获异常”,导致程序终止。

如果当前catch语句的参数与抛出的对象类型不匹配呢?

来看以下代码:

#include <iostream>
using namespace std;void func3()
{try{int x = 0;if (x == 0){throw x;}}catch (float a){cout << "func3" << endl;}
}void func2()
{try{func3();}catch (int x){cout << "func2" << endl;}
}void func1()
{try{func2();}catch (int x){cout << "func1" << endl;}
}int main()
{try{func1();}catch (char c){cout << "main" << endl;}return 0;
}

上述代码中,main函数调用func1函数,func1函数调用func2函数,func2函数调用func3函数。func3抛出一个类型为int的异常,很明显,与catch的float类型不匹配。此时程序会沿着整个调用链依次查找catch,直到找到匹配的类型,再执行相应catch语句

我们看下运行结果:

可以看到,func2捕获了异常。这里虽然func1的catch语句的参数也为int,但是程序沿着调用链查找时,还没有查找到fun1的位置。所以被执行的catch语句块是在调用链中与抛出对象匹配且距离最近的那一个。程序沿着调用链进行查找的过程称之为栈展开

那么如果调用链中所有的catch参数类型都与抛出对象不匹配呢?那么程序会调用标准库中的terminate函数,进而终止程序。但在大型项目中,不发生严重错误的情况下,我们不希望程序终止,所以一般会在main函数中写一个最终捕获(catch(...)),它可以捕获任意类型的异常。

int main()
{try{func1();}catch (...)//最终捕获{//...}
}

注意:如果throw匹配到了调用链中其他函数的catch语句,那么沿着该调用链中没有匹配到的函数,其栈帧都将销毁:

由于本函数内catch匹配失败会导致栈帧销毁,所以throw抛出的对象其实是一份拷贝,该拷贝对象会在匹配成功的catch语句块结束之后销毁。(类似于函数传值返回)

三、catch语句匹配的特例

        除了要求类型相同之外,catch在一些其他情况也允许参数匹配,例如:

  • 普通对象--被const对象捕获

  • 数组--被数组元素类型的指针捕获

  • 函数--被指向同类型函数的指针捕获

  • 派生类--被基类捕获(非常实用)

四、异常的重新抛出

        有些时候的异常在抛出之后,需要进行一些矫正,再交给调用链中其他函数的catch去处理。这时我们就可以在本函数当中先捕获该异常,待处理完成之后,再次抛出该异常。

示例代码:

void func()
{try{int x = 0;//...throw x;//抛出异常}catch (const char* s){//进行一些操作之后再次抛出该异常,交给其他catch捕获处理throw;}
}

五、异常安全问题

        有时我们动态申请了某些资源,但可能因为抛出异常,未执行后续资源清理的代码而导致内存泄漏、死锁或文件未关闭的问题。此时可以在本函数中使用最终捕获,先接收任意类型异常,处理资源清理问题,最后重新抛出该异常。

        对于更复杂的情况:如申请多个资源,其中因一个资源抛出异常,但最终捕获又释放了所有资源;另一个资源抛出异常,最终捕获又进行多次释放等等。这时考虑使用智能指针(博主会在后续文章中介绍)

六、异常规范

        实际开发中,预先知道某个函数是否会抛出异常很有好处,方便处理。我们在函数参数列表之后加入C++11关键字noexcept,表示该函数不会抛出异常。如果函数抛出了异常,会导致程序终止。

注意:并不会在编译时检查noexcept函数是否带有throw语句,而是在程序运行时按照是否真的抛出了异常来进行判断。

        noexcept(expression)也可以检查一个表达式(或函数,传入函数地址)是否可能抛出异常,如果可能,返回false,否则返回true。

七、标准库异常体系

        c++标准库定义了一套自己的异常体系库,包含在头文件<exception>中。

标准异常体系库查阅:exception - C++ Reference

我们需要使用它时,用基类exception捕获异常,然后调用what()函数打印错误信息即可。

示例代码:

#include <iostream>
#include <exception>
using namespace std;int main()
{try{int x = 0;cin >> x;if (x == 0){throw exception("输入错误");}cout << "正常" << endl;}catch (exception& e){cout << e.what() << endl;}return 0;
}

运行结果:

总结

        本篇文章,我们主要学习了异常的使用方法及其处理机制。相比c语言错误码的方式,c++的异常可以更加全面地处理程序出现的问题。但异常也有些缺点,例如造成程序执行混乱,内存泄漏等等。之后博主会和大家介绍c++的智能指针,它可以解决异常容易造成内存泄漏的问题。如果你觉得博主讲的还不错,就请留下一个小小的赞在走哦,感谢大家的支持❤❤❤


http://www.ppmy.cn/embedded/174623.html

相关文章

Milvus vs. ElasticSearch:向量库检索性能测试

目录 1. 构建检索库2. 测试条件3. 测试结果4. 性能分析5. 结论 1. 构建检索库 构建通用场景库总计约2万张。构建车辆数据库总计约12万张。构建公共数据库&#xff0c;包括Flickr30k、COCO、nlvr2、vqa等数据集约43万张。 2. 测试条件 环境说明&#xff1a;分别单机部署Milvu…

SVN简明教程——下载安装使用

SVN教程目录 一、开发中的实际问题二、简介2.1 版本控制2.2 Subversion2.3 Subversion的优良特性2.4 工作原理2.5 SVN基本操作 三、Subversion的安装与配置1. 服务器端程序版本2. 下载源码包3. 下载二进制安装包4. 安装5. 配置版本库① 为什么要配置版本库&#xff1f;② 创建目…

STM32 —— MCU、MPU、ARM、FPGA、DSP

在嵌入式系统中&#xff0c;MCU、MPU、ARM、FPGA和DSP是核心组件&#xff0c;各自在架构、功能和应用场景上有显著差异。以下从专业角度详细解析这些概念&#xff1a; 一、 MCU&#xff08;Microcontroller Unit&#xff0c;微控制器单元&#xff09; 核心定义 集成系统芯片&a…

【2025】部标JT808、JT1078、苏标主动安全模拟工具

支持JT808-2013、JT808-2019 除了直接客户端&#xff0c;还支持JT808服务端程序、JT808报文解析 下载地址&#xff1a;https://www.lingx.com/download.html

VSCODE右下角切换环境没用

VSCODE惦记右下角python版本&#xff0c;切换别的虚拟环境时&#xff0c;始终切换不了&#xff0c;同时右下角弹出&#xff1a; Client Pylance: connection to server is erroring. 取消继承环境也改了。https://www.cnblogs.com/coreylin/p/17509610.html 还是不行&#xf…

2025-03-19 学习记录--C/C++-C语言-单链表的按位查找和按值查找

C语言-单链表的按位查找和按值查找 ⭐️ 单链表的结构体定义&#xff0c;请查看我的另一篇文章。 一、C语言-单链表的按位查找 &#x1f36d; 【题目】有一个带头结点的单链表L&#xff0c;请设计一个算法查找其第i个结点位置&#xff0c;若存在则返回该结点的指针&#xff0c;…

数据结构-----队列

顺序队列&#xff08;Queue&#xff09; 一、队列核心概念 1. 基本特性 先进先出&#xff08;FIFO&#xff09;&#xff1a;最早入队的元素最先出队操作限制&#xff1a; 队尾&#xff08;Rear&#xff09;&#xff1a;唯一允许插入的位置队头&#xff08;Front&#xff09;&…

SAP 附件增删改查与文件服务器交互应用

【需求背景】 非SAP标准附件应用&#xff0c;自定义一套&#xff0c;跟公司内部文档服务器交互&#xff0c;支持各个应用场景的附件增删改查等。 每个附件在文件服务器上都有一个文件唯一ID作为关键字。 应用分两块&#xff1a;SAP GUI端&#xff0c;跟WDA Portal端应用 GU…