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

【c++】异常处理

一、什么是异常

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

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

二、异常的使用

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

示例代码:

代码语言:javascript

AI代码解释

#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语句的参数与抛出的对象类型不匹配呢?

来看以下代码:

代码语言:javascript

AI代码解释

#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(...)),它可以捕获任意类型的异常。

代码语言:javascript

AI代码解释

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

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

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

三、catch语句匹配的特例

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

  • 普通对象--被const对象捕获
  • 数组--被数组元素类型的指针捕获
  • 函数--被指向同类型函数的指针捕获
  • 派生类--被基类捕获(非常实用)

四、异常的重新抛出

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

示例代码:

代码语言:javascript

AI代码解释

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()函数打印错误信息即可。

示例代码:

代码语言:javascript

AI代码解释

#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.jsqmd.com/news/733724/

相关文章:

  • MCP 2026医疗数据安全防护“红蓝对抗”实战手册(内部流出版):覆盖CT/MRI/病理全模态攻击链与17个防御卡点
  • 01 用栈实现队列
  • 大气层系统完整指南:Switch自定义固件的终极解决方案
  • Moonlight-Switch:Nintendo Switch游戏串流技术方案与多平台兼容架构
  • taotoken 平台 python 调用 openai 兼容 api 的完整入门指南
  • 借助模型广场与官方折扣为新项目选择高性价比模型
  • 解锁旧Mac新生命:OpenCore Legacy Patcher完全指南
  • C++中string常用方法总结
  • 2026年扬州工厂短视频代运营案例分析 - 速递信息
  • 2026企业AI陪跑推荐:全程陪伴,落地见效 8 - 速递信息
  • 【Laravel AI Security Alert】:2026年Q1已爆发7起Prompt注入+模型越权调用事件,3步修复框架层RCE风险(附CVE-2026-XXXX PoC)
  • Laravel 12模型层AI增强成本封顶设计:3种可插拔式Token配额策略,让每个Eloquent操作自带预算守门员
  • 别再乱配CORS了!Flask-CORS从入门到生产环境安全配置实战(含Nginx反向代理)
  • 基于AI与现金流模拟的自托管个人财务预测机器人开发实践
  • CompressO:如何用这款免费开源工具将视频图片压缩90%以上
  • 为AI代码生成器Cursor配置ESLint与Prettier规则集,实现自动化代码规范检查与格式化
  • 2026连云港黄金回收市场深度解析与靠谱品牌推荐 - 速递信息
  • 【黑马点评日记】异步秒杀:异步线程和阻塞队列以及Lua脚本的相关流程分析
  • R语言偏见检测不可绕过的5个统计陷阱,第3个让OpenAI内部报告延迟发布117天
  • EpiCaR集成学习:动态修正认知不确定性的高效推理方法
  • 【Swoole × LLM 企业级落地白皮书】:3类高敏业务(智能工单、实时投顾、IoT边缘推理)的长连接架构选型决策树与SLA保障方案
  • 多模态模型小型化:挑战与优化策略
  • 2026真心问:重庆本地家教哪家靠谱? - 速递信息
  • 2026唯品会礼品卡回收平台TOP榜:鼎鼎收专业深耕15年,四项五星实力登顶 - 鼎鼎收礼品卡回收
  • 2026年必知!揭秘霞浦美食地道店铺,究竟藏着哪些好用秘诀? - GrowthUME
  • 从纸质到数字:用Audiveris让古老乐谱重获新生的魔法
  • C++11新特性大揭秘:优化性能与简化代码的利器
  • ncmdump终极指南:3分钟解锁网易云音乐加密文件的完整解决方案
  • 1G/2.5G Ethernet PCS/PMA or SGMII IP核(五)
  • packer详解