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

【C++第二十四章】异常

前言 C的异常机制本质上是在回答一个非常现实的问题当函数已经无法在当前位置继续处理错误时应该怎样把错误交给更高层、更合适的位置处理。如果只依赖返回值层层上报那么调用链一长代码就会迅速充满判断、转发和补救逻辑业务主线也会被大量错误处理代码切碎。异常机制提供了另一种思路在出错点直接throw然后沿调用栈向外寻找能处理它的catch。这样正常逻辑和错误处理逻辑就能在结构上分开调用者也不需要在每一层都手动检查并转发错误码。但异常并不是只有优点。它一方面让错误处理更集中、更自然另一方面也带来了执行流跳转、资源管理、异常安全和接口规范等问题。真正把这部分学明白关键不只是会写try / catch / throw而是要建立一条完整主线异常是如何抛出、如何沿栈传播、如何匹配捕获、如何保证资源不泄漏以及为什么工程里总会强调RAII和noexcept。一. 异常机制到底在解决什么问题 异常是一种错误处理机制。它适合处理这样的场景当前函数检测到了错误但这个错误既不适合在本地吞掉也不适合简单返回一个值草草了事而应该交给更高层做统一决策。1.1throw、try、catch的职责分工throw抛出异常对象try标记可能出错、需要保护的代码块catch在合适位置捕获并处理对应异常1.2 和错误码方式相比本质区别是什么错误码方案要求每一层函数都主动检查返回值再决定是否继续向外返回而异常方案则允许错误主动跳出当前执行流一直传播到真正愿意处理它的位置。因此异常机制的核心优势并不是“写法更高级”而是把正常业务流程和错误处理流程解耦。二.try / catch / throw的基本执行流 2.1 一旦throw当前后续语句就不再执行doubleDivision(intlen,inttime){if(time0){throw除0错误!;}return(double)len/(double)time;}这里一旦time 0程序就不会再执行后面的return而是直接离开当前正常执行流开始寻找匹配的catch。2.2try中抛出异常后会直接跳到匹配的catchintmain(){try{func();}catch(constchar*str){coutstrendl;}}只要try保护的代码范围内抛出了匹配异常就会立刻停止异常点之后的普通语句执行直接转入对应的catch块。2.3catch处理完后程序从哪里继续当某个catch成功处理异常后程序不会回到原来throw的位置继续执行而是从整个try-catch结构之后继续往下走。 避坑指南异常不是“临时暂停再回来”而是彻底切换执行路径。一旦抛出异常点之后的普通语句就不会再执行。三. 栈展开为什么进入catch之前会先析构对象 异常最关键、也最容易考的一点就是栈展开。3.1 什么是栈展开当异常从当前函数向外传播时程序会沿着函数调用栈一层层退出当前作用域。这个退出过程里已经构造完成的局部对象会按作用域逆序析构。3.2 为什么对象会先析构再进入catchvoidfunc(){A aa;intlen,time;cinlentime;coutDivision(len,time)endl;}若Division(len, time)抛出异常那么func()不会直接瞬移到外层catch而是会先退出func()当前作用域。退出前局部对象aa要先析构然后异常再继续向外传播。3.3 这一步为什么极其重要因为它直接决定了局部对象管理的资源能否在异常路径下自动释放。这也是RAII能成立的根本基础。 避坑指南异常传播时不是简单“跳过代码”而是伴随完整的作用域清理过程。也正因为如此局部对象比裸new更适合在异常环境中管理资源。四. 异常是如何沿调用栈匹配catch的 4.1 先看当前作用域再沿调用链向外找异常抛出后并不是全局乱跳而是按非常明确的顺序寻找处理者先检查当前函数里是否存在匹配的try-catch若没有退出当前函数栈帧继续到调用者函数中寻找一直向外传播到main若最终仍无匹配处理者程序直接终止4.2 哪个catch会被命中会命中的是调用链上距离抛出点最近、且类型匹配的那个catch。4.3 如果没有任何匹配的catch程序会直接终止。为了兜底也可以提供catch(...){cout未知异常endl;}4.4catch(...)的意义它表示“捕获任意类型异常”适合做最后一道防线避免程序因为完全未处理的异常直接崩溃。但它的问题也很明显你能兜住异常却不一定知道异常到底是什么。五. 异常对象与匹配规则为什么大型项目常配合继承和多态 小例子里常直接抛字符串或整数但实际工程里更常见的做法是抛异常类对象。5.1 为什么抛对象更合理因为对象可以携带更丰富的信息例如错误类别错误码错误描述上下文信息额外调试信息这样比单纯返回-1或抛一个error更有表达力。5.2 为什么会配合继承体系使用大型系统里错误类型很多。如果每种异常都单独随便定义最终会变得非常混乱。更自然的做法是设计一套层次化异常体系基类统一抽象异常派生类细分不同类型错误这样做的好处是可以按细粒度类型分别捕获也可以用基类统一兜底异常体系更容易扩展和规范化5.3 捕获时为什么常建议用引用catch(constMyExceptione){// ...}这样可以避免额外拷贝同时保留多态行为。六. 异常安全问题为什么异常一跳裸资源就容易泄漏 ⚠️异常最危险的一点不在于“程序会报错”而在于执行流中断后原本依赖手工收尾的资源释放逻辑可能根本来不及执行。6.1 一个典型问题new了以后还没delete异常先发生了voidfunc(){int*arrnewint[10];intlen,time;cinlentime;coutDivision(len,time)endl;delete[]arr;}如果Division(len, time)抛异常那么delete[] arr;就不会执行。这样就发生了内存泄漏。6.2 为什么局部对象比裸指针安全得多因为局部对象会参与栈展开在异常传播时自动析构而裸指针只是一个普通变量不会自动替你释放其指向的堆资源。6.3 一种补救方式在局部内部再try-catchvoidfunc(){int*arrnewint[10];try{// ...}catch(...){delete[]arr;throw;}}这种写法能补救但一旦资源变多、代码变复杂维护成本会迅速上升。6.4 更根本的解决方案RAII真正稳妥的办法不是到处手动补catch收尾而是把资源交给对象管理例如容器智能指针资源管理类这样只要异常发生对象析构就会自然完成清理。 避坑指南异常安全的核心不是“多写几个 catch”而是“尽量不要让资源依赖手工收尾”。一旦资源生命周期交给对象管理异常路径会安全很多。七. 重新抛出为什么清理后还能把异常继续交给上层 有时当前层并不真正负责处理异常只负责做局部清理然后仍希望把异常交给更高层决策。这时就可以catch(...){// 局部清理throw;}7.1 重新抛出的作用当前层做资源补救不吞掉异常让更外层继续按统一策略处理7.2 它适合什么场景适合“当前层知道怎么收尾但不知道该怎么处理业务后果”的情况。例如回滚局部状态释放临时资源打印局部日志再把异常继续交给上层八. 构造函数和析构函数为什么最好不要抛异常 8.1 析构函数抛异常尤其危险因为析构函数常常发生在栈展开过程中。若一个异常正在传播析构函数又抛出第二个异常程序通常会直接终止后果很难控制。8.2 构造函数为什么也要谨慎构造过程一旦中途失败对象尚未完整建立资源状态可能处于部分初始化状态。此时若设计不当也会让资源管理和状态恢复变得更复杂。8.3noexcept的意义C11引入了noexcept用于显式说明函数不会抛异常thread()noexcept;thread(threadx)noexcept;8.4 它不只是“写给人看”的标记noexcept既能表达接口承诺也会影响编译器和标准库的优化与行为选择。例如某些容器在移动元素时会优先考虑“移动操作是否保证不抛异常”。 避坑指南析构函数最稳妥的默认原则就是不要让异常逃出去。构造失败可以抛但析构阶段若再抛异常风险会高很多。九. 异常规范与接口约束为什么“会不会抛、抛什么”也属于接口语义 一个函数除了“做什么”还应该尽量明确“出错时会怎样”。否则调用者很难正确使用它。9.1 旧式异常规范和noexcept旧时代曾有voidfunc()throw(A,B,C);voidfunc2()throw();后来的主流写法则更多依赖voidfunc()noexcept;9.2 为什么工程里会强调规范异常体系因为如果没有统一约束最终会出现这些问题各模块随意抛不同类型调用者不知道该捕获什么接口语义混乱使用方调试成本很高更稳妥的思路通常是异常类型尽量有统一基类异常信息表达尽量规范明确哪些接口允许抛异常明确哪些接口必须noexcept十. 异常和错误码到底怎么取舍 ️异常机制并不是为了彻底消灭错误码而是适合某些更自然的错误传播场景。10.1 什么时候异常更合适构造函数出错运算符重载出错深层调用链中的严重失败不适合每层都手工检查返回值的场景希望集中处理错误逻辑的场景10.2 什么时候错误码仍然常见低层系统接口高性能敏感路径需要稳定可控的本地失败分支历史代码或跨语言接口团队整体约定偏向显式错误返回的场景10.3 二者最核心的区别方式特点错误码显式、直观、每层都要处理或转发异常传播自然、处理集中、执行流会跳转十一. 异常的优点与代价 11.1 优点错误信息表达能力更强能把正常逻辑和错误逻辑分开不需要层层手工返回错误码更适合构造函数、运算符等不方便返回错误码的场景有利于大型项目做统一错误处理11.2 代价执行流跳转更突然调试理解成本更高不规范使用时容易造成资源泄漏需要更严格的资源管理意识不同模块若异常体系混乱接口体验会很差存在一定运行时与实现复杂度成本总结 异常机制真正要解决的并不是“怎么报错”这么简单而是当错误已经不适合在当前位置处理时如何把它安全、清晰、可扩展地交给更高层处理。顺着这条主线看整章内容逻辑会非常清楚throw负责抛出错误try / catch负责建立保护区和处理入口栈展开保证局部对象会在传播过程中析构匹配规则决定异常沿调用链寻找最近且合适的catch继承体系让异常类型能组织得更规范裸资源在异常路径下容易泄漏因此必须重视异常安全RAII和智能指针本质上是在解决异常路径的资源管理问题构造、析构和noexcept又进一步约束了哪些接口能安全参与异常体系因此异常这部分最重要的结论可以压缩成一句话异常机制的价值不只是“跳到 catch”而是“在执行流跳跃的前提下仍然保证错误传播清晰、资源释放安全、接口语义可控”。真正把这句话理解透了后面再看RAII、智能指针、标准库异常类、异常安全等级整条线就会自然接上。

相关文章:

【C++第二十四章】异常

前言 🚀C 的异常机制,本质上是在回答一个非常现实的问题:当函数已经无法在当前位置继续处理错误时,应该怎样把错误交给更高层、更合适的位置处理。 如果只依赖返回值层层上报,那么调用链一长,代码就会迅速充…...

x86汇编堆栈第二个案例

x86汇编堆栈第二个案例x86汇编堆栈第二个案例 1)案例介绍 咱们上节课先把常见的x86下的堆栈过了一遍,包括基本指令对吧,除了上一个案例咱们还可以做什么使用现在学到的内容?既然咱们知道了“后进先出(LIFO)…...

x86汇编堆栈

x86汇编堆栈 1)堆栈操作 x86汇编中的堆栈是一块特殊的内存区域,用于存储程序运行时的数据。它遵循"后进先出LIFO的原则",主要用于函数调用时的参数传递、局部变量存储以及保存返回地址。 堆栈操作的核心指令是PUSH和POP。PUSH指令将…...

别再自己造轮子了!用InsightFace+FastAPI快速搭建一个高精度人脸识别Web服务

基于InsightFace的高精度人脸识别Web服务实战指南 1. 为什么选择InsightFace构建人脸识别服务 三年前我接手一个考勤系统项目时,曾花费两个月时间从零训练FaceNet模型,结果上线后遇到戴眼镜员工识别率骤降的问题。直到发现InsightFace这个开源项目&#…...

x86汇编如何使用伪指令实现if,else,while,dowhile,switch-case

x86汇编如何使用伪指令实现if,else,while,dowhile,switch-case 1)汇编伪指令介绍 伪指令是汇编器提供的语法规则,它主要为程序员提供语法糖简化汇编代码的编写。常见的伪指令包括条件汇编类(IF&…...

AI率从90%降到合格线,我踩了3个坑后找到的方法

我的论文AI率在知网检出了91%。 最后我把AI率降到了9%,但在这之前踩了3个坑,多花了将近两天时间。这篇文章不是炫成绩,是把这3个坑说清楚,让后来的人少走一段弯路。 坑一:花了一天手动改写,基本没用 拿到…...

AI率85%的论文,这款工具降完后我直接去答辩了

答辩前5天,知网AIGC检测报告出来了:AI率85%。 我是那种遇到问题喜欢先搜索再行动的人,所以花了两个小时看了很多经验帖。最终决策:不手改,直接上比话降AI。 结果:11%,答辩前3天处理完&#xf…...

AI率85%降到15%的完整操作流程,一步一步来

AI率85%,这不是个小问题,但也不是无解的问题。我把整个处理流程拆成了具体步骤,照着来就行,没什么复杂的。 在开始之前,明确一件事 AI率85%的论文,靠手动修改降到15%,从理论上讲是可行的&…...

AI率80%+送去降AI工具处理,3款结果对比

这篇文章记录的是一个横向测试:找了几篇AI率都在80%以上的论文,分别送去嘎嘎降AI、比话降AI、率零处理,然后统一在知网检测,看最终结果。 测试设计 测试论文(4篇): 编号专业字数知网AI率&…...

2026届毕业生推荐的AI学术工具实测分析

Ai论文网站排名(开题报告、文献综述、降aigc率、降重综合对比) TOP1. 千笔AI TOP2. aipasspaper TOP3. 清北论文 TOP4. 豆包 TOP5. kimi TOP6. deepseek 于毕业论文写作进程里,人工智能工具可充作辅助方式用以提高效率。学生能借AI开展…...

OpenCV直线检测避坑指南:HoughLinesP参数调优实战(Python版)

OpenCV直线检测避坑指南:HoughLinesP参数调优实战(Python版) 在计算机视觉项目中,直线检测往往是基础却关键的一环。无论是自动驾驶中的车道线识别,还是工业质检中的零件尺寸测量,亦或是文档扫描应用中的表…...

YimMenu终极指南:5分钟学会GTA5最强安全增强工具

YimMenu终极指南:5分钟学会GTA5最强安全增强工具 【免费下载链接】YimMenu YimMenu, a GTA V menu protecting against a wide ranges of the public crashes and improving the overall experience. 项目地址: https://gitcode.com/GitHub_Trending/yi/YimMenu …...

Steam Depot清单自动化工具:Onekey实现游戏数据高效管理的完整方案

Steam Depot清单自动化工具:Onekey实现游戏数据高效管理的完整方案 【免费下载链接】Onekey Onekey Steam Depot Manifest Downloader 项目地址: https://gitcode.com/gh_mirrors/one/Onekey 在游戏开发与玩家社区中,获取和管理Steam游戏清单一直…...

3大终极方案解决Amlogic设备U盘启动难题:从故障诊断到系统优化的完整指南

3大终极方案解决Amlogic设备U盘启动难题:从故障诊断到系统优化的完整指南 【免费下载链接】amlogic-s9xxx-armbian Supports running Armbian on Amlogic, Allwinner, and Rockchip devices. Support a311d, s922x, s905x3, s905x2, s912, s905d, s905x, s905w, s90…...

如何用DS4Windows让PS手柄在PC游戏世界畅通无阻

如何用DS4Windows让PS手柄在PC游戏世界畅通无阻 【免费下载链接】DS4Windows Like those other ds4tools, but sexier 项目地址: https://gitcode.com/gh_mirrors/ds/DS4Windows 你是否曾遇到这样的尴尬场景:购买了心仪的PS4或PS5手柄,想在PC上畅…...

云效流水线+K8s实战:Java微服务全自动部署与优化指南(手把手版)

1. 云效流水线入门:从零搭建Java微服务CI/CD管道 第一次接触云效流水线时,我像发现新大陆一样兴奋——原来部署可以这么简单!记得去年团队还在用Jenkins手动打包部署,每次发版都要折腾到凌晨。现在用云效 K8s的组合,我…...

从Hyper-V到内核隔离:手把手教你为eNSP在Win11 24H2上‘清场’(安全功能关闭指南)

从Hyper-V到内核隔离:Win11 24H2深度虚拟化冲突解决手册 当你在Windows 11 24H2上启动eNSP模拟器时,那个令人沮丧的"版本不兼容"提示背后,隐藏着一场现代系统安全机制与传统虚拟化工具的无声战争。这不是简单的软件冲突&#xff0c…...

SpringBoot + 本地事务表 + 定时扫描补偿:轻量级方案实现最终一致性,无中间件依赖

在分布式系统中,数据一致性是一个永恒的话题。传统的分布式事务解决方案如 Seata、XA 等往往需要引入重量级中间件,增加了系统复杂度和运维成本。 本文将介绍一种轻量级的最终一致性方案——本地事务表 + 定时扫描补偿,该方案: 零中间件依赖:不需要 MQ、Seata 等外部组件…...

物联网设备上高德地图离线地图加载慢?5秒内快速加载的终极解决方案

物联网设备高德地图离线加载优化实战:从2分钟到5秒的进阶方案 在智能电表、车载终端、工业传感器等物联网设备中,离线地图的快速加载直接影响着用户体验与系统响应效率。我们曾遇到一个典型场景:某共享单车智能锁通过4G模块上报位置时&#x…...

利用drawio高效绘制数据库ER图:从入门到精通

1. 为什么选择drawio绘制数据库ER图 第一次接触数据库设计时,我被各种专业工具的价格和复杂度吓退了。直到发现drawio这个宝藏工具,才真正体会到什么叫"用最简单的工具做专业的事"。作为一款完全免费的跨平台工具,drawio不仅支持网…...

JAVA重点基础、进阶知识及易错点总结(36)Lombok 实战 + 阶段总结

🚀 Java 巩固进阶 第 36 天 主题:Lombok 实战 阶段总结 —— 解放双手,高效开发📅 进度概览:今天是 设计模式与注解阶段(31-36 天)的最后一天,也是 Java 基础巩固阶段的收官之日&a…...

论文AI率80%+的紧急处理方案,答辩前用得上

距离答辩3天,AI率检出80%——这是最糟糕的时间点碰到最糟糕的问题。 不要慌,这个情况有成熟的处理方案,我见过很多人在这个时间节点成功降下来的。下面是紧急情况下的处理方法,按照时间紧迫程度分了几个场景。 先做一个判断&…...

JAVA重点基础、进阶知识及易错点总结(35)注解与反射

🚀 Java 巩固进阶 第 35 天 主题:注解与反射结合 —— 让注解"活"起来📅 进度概览:继昨天学习注解定义之后,今天进入 注解的核心应用场景:注解 反射。单独的注解只是"标签"&#xff…...

AI率超80%不要慌,这样处理比自己改快10倍

看到AI率80%,第一反应是慌乱,这完全正常。但慌乱之后,做什么决定很关键。 这篇文章只说一件事:为什么用工具处理比自己改快10倍,怎么用工具最快解决这个问题。 手动改写的真实速度 先来做一个计算。 一个写作速度正…...

JAVA重点基础、进阶知识及易错点总结(34)注解基础(Annotation)

🚀 Java 巩固进阶 第 34 天 主题:注解基础(Annotation)—— 代码的"元数据"标签📅 进度概览:继设计模式之后,今天学习 Java 注解体系。注解是"代码的标签",是 …...

AI 编程 Harness 框架深度拆解(非常详细),6 大框架从入门到精通,收藏这一篇就够了!

AI 会写,不等于 AI 能稳定交付。 前段时间我们都在说 Vibe Coding,大家都知道是氛围编程的意思,但是现在也有叫“直觉编程”。什么叫直觉编程,就是完全不用管其它的,想到什么就做什么,主打一个靠直觉写代码…...

多模态整合进阶必读:MIT APOLLO框架核心思想(非常详细),从原理到精通,收藏这一篇就够了!

麻省理工学院与瑞士苏黎世联邦理工学院的联合研究团队,提出了计算框架 APOLLO,即通过潜变量优化学习部分重叠潜空间的自编码器,其通过显式建模共享信息和模态特异性信息,为更全面、精准地解析细胞状态及其调控逻辑提供了一条可行的…...

初试FreeRTOS:创建上位机接收数据驱动4个舵机任务,如裸机般无感

解析函数上位机数据协议:协议格式 (LD150舵机)[0x55][0x55][ID][长度][命令][数据...][校验和]2字节 1字节 1字节 1字节 N字节 1字节帧头: 0x55 0x55 ID: 舵机ID (1-4) 或 0xFE (广播) 数据: 每组5字节 ID time_low time_high pos_low pos_high 位置: …...

GraphRAG退场了,BookRAG知识像翻书一样简单

你是否曾面对一本厚厚的说明书、技术手册或学术著作,在寻找某个具体信息时感到无从下手?传统的检索增强生成(RAG)方法在处理这类结构复杂的长文档时,常常力不从心。它们要么将文档视为一盘散沙的文本,丢失了…...

7张图讲透Claude Code架构(非常详细),Harness设计从入门到精通,收藏这一篇就够了!

1. 整体概述 众所周知,Claude Code不仅仅是Coding产品,更是一个通用的终端Agent:能循环思考、调度工具、治理权限、恢复上下文、稳定长会话… 如何研读项目源码呢? 首先,我让AI帮着梳理了下目录架构和模块职责&…...