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

news2026/4/5 22:35:05
前言 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、智能指针、标准库异常类、异常安全等级整条线就会自然接上。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2487106.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SpringBoot-17-MyBatis动态SQL标签之常用标签

文章目录 1 代码1.1 实体User.java1.2 接口UserMapper.java1.3 映射UserMapper.xml1.3.1 标签if1.3.2 标签if和where1.3.3 标签choose和when和otherwise1.4 UserController.java2 常用动态SQL标签2.1 标签set2.1.1 UserMapper.java2.1.2 UserMapper.xml2.1.3 UserController.ja…

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站,会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后,网站没有变化的情况。 不熟悉siteground主机的新手,遇到这个问题,就很抓狂,明明是哪都没操作错误&#x…

网络编程(Modbus进阶)

思维导图 Modbus RTU(先学一点理论) 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议,由 Modicon 公司(现施耐德电气)于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…

UE5 学习系列(二)用户操作界面及介绍

这篇博客是 UE5 学习系列博客的第二篇,在第一篇的基础上展开这篇内容。博客参考的 B 站视频资料和第一篇的链接如下: 【Note】:如果你已经完成安装等操作,可以只执行第一篇博客中 2. 新建一个空白游戏项目 章节操作,重…

IDEA运行Tomcat出现乱码问题解决汇总

最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…

利用最小二乘法找圆心和半径

#include <iostream> #include <vector> #include <cmath> #include <Eigen/Dense> // 需安装Eigen库用于矩阵运算 // 定义点结构 struct Point { double x, y; Point(double x_, double y_) : x(x_), y(y_) {} }; // 最小二乘法求圆心和半径 …

使用docker在3台服务器上搭建基于redis 6.x的一主两从三台均是哨兵模式

一、环境及版本说明 如果服务器已经安装了docker,则忽略此步骤,如果没有安装,则可以按照一下方式安装: 1. 在线安装(有互联网环境): 请看我这篇文章 传送阵>> 点我查看 2. 离线安装(内网环境):请看我这篇文章 传送阵>> 点我查看 说明&#xff1a;假设每台服务器已…

XML Group端口详解

在XML数据映射过程中&#xff0c;经常需要对数据进行分组聚合操作。例如&#xff0c;当处理包含多个物料明细的XML文件时&#xff0c;可能需要将相同物料号的明细归为一组&#xff0c;或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码&#xff0c;增加了开…

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器的上位机配置操作说明

LBE-LEX系列工业语音播放器|预警播报器|喇叭蜂鸣器专为工业环境精心打造&#xff0c;完美适配AGV和无人叉车。同时&#xff0c;集成以太网与语音合成技术&#xff0c;为各类高级系统&#xff08;如MES、调度系统、库位管理、立库等&#xff09;提供高效便捷的语音交互体验。 L…

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)

题目&#xff1a;3442. 奇偶频次间的最大差值 I 思路 &#xff1a;哈希&#xff0c;时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况&#xff0c;哈希表这里用数组即可实现。 C版本&#xff1a; class Solution { public:int maxDifference(string s) {int a[26]…

【大模型RAG】拍照搜题技术架构速览:三层管道、两级检索、兜底大模型

摘要 拍照搜题系统采用“三层管道&#xff08;多模态 OCR → 语义检索 → 答案渲染&#xff09;、两级检索&#xff08;倒排 BM25 向量 HNSW&#xff09;并以大语言模型兜底”的整体框架&#xff1a; 多模态 OCR 层 将题目图片经过超分、去噪、倾斜校正后&#xff0c;分别用…

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…

接口测试中缓存处理策略

在接口测试中&#xff0c;缓存处理策略是一个关键环节&#xff0c;直接影响测试结果的准确性和可靠性。合理的缓存处理策略能够确保测试环境的一致性&#xff0c;避免因缓存数据导致的测试偏差。以下是接口测试中常见的缓存处理策略及其详细说明&#xff1a; 一、缓存处理的核…

龙虎榜——20250610

上证指数放量收阴线&#xff0c;个股多数下跌&#xff0c;盘中受消息影响大幅波动。 深证指数放量收阴线形成顶分型&#xff0c;指数短线有调整的需求&#xff0c;大概需要一两天。 2025年6月10日龙虎榜行业方向分析 1. 金融科技 代表标的&#xff1a;御银股份、雄帝科技 驱动…

观成科技:隐蔽隧道工具Ligolo-ng加密流量分析

1.工具介绍 Ligolo-ng是一款由go编写的高效隧道工具&#xff0c;该工具基于TUN接口实现其功能&#xff0c;利用反向TCP/TLS连接建立一条隐蔽的通信信道&#xff0c;支持使用Let’s Encrypt自动生成证书。Ligolo-ng的通信隐蔽性体现在其支持多种连接方式&#xff0c;适应复杂网…

铭豹扩展坞 USB转网口 突然无法识别解决方法

当 USB 转网口扩展坞在一台笔记本上无法识别,但在其他电脑上正常工作时,问题通常出在笔记本自身或其与扩展坞的兼容性上。以下是系统化的定位思路和排查步骤,帮助你快速找到故障原因: 背景: 一个M-pard(铭豹)扩展坞的网卡突然无法识别了,扩展出来的三个USB接口正常。…

未来机器人的大脑:如何用神经网络模拟器实现更智能的决策?

编辑&#xff1a;陈萍萍的公主一点人工一点智能 未来机器人的大脑&#xff1a;如何用神经网络模拟器实现更智能的决策&#xff1f;RWM通过双自回归机制有效解决了复合误差、部分可观测性和随机动力学等关键挑战&#xff0c;在不依赖领域特定归纳偏见的条件下实现了卓越的预测准…

Linux应用开发之网络套接字编程(实例篇)

服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …

华为云AI开发平台ModelArts

华为云ModelArts&#xff1a;重塑AI开发流程的“智能引擎”与“创新加速器”&#xff01; 在人工智能浪潮席卷全球的2025年&#xff0c;企业拥抱AI的意愿空前高涨&#xff0c;但技术门槛高、流程复杂、资源投入巨大的现实&#xff0c;却让许多创新构想止步于实验室。数据科学家…

深度学习在微纳光子学中的应用

深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向&#xff1a; 逆向设计 通过神经网络快速预测微纳结构的光学响应&#xff0c;替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…