C++异常处理系列从基础到通天(一篇即毕业系列)
文章目录异常处理基本概念定义关键字用法代码块代码块C标准异常类层次结构标准异常类层次结构示例自定义异常类示例noexcept验证函数是否 noexcept什么时候使用 noexcept异常安全性异常跨函数传播示例Copy-and-Swap 惯用法与异常安全基本模式异常安全流程拆解关键要点首先effective c中条款第29提到请记住1. 异常安全函数 Exception-safe functions即使发生异常也不会泄露资源或允许任何数据类型败坏。这样的函数区分为三种可能的保证基本型 强烈型 不抛异常型2. “强烈保证” 往往能够以 copy-and-swap 实现出来 但 “强烈保证” 并非对所有函数都可实现或具备现实意义3. 函数提供的“异常安全保证”通常最高只等于其所调用之各个函数的 “异常安全保证”中的最弱者我需要告诉你本篇甚至大部分的异常处理都围绕基本型异常。强烈型异常基本就用copy-and-swap见文章最后即可不抛异常很简单就是noexcept关键字。详细见下文注意别小看以上内容如果你要真正成为一名程序员一定需要先成为一位异常处理大师本系列所有内容都如同本篇一样优秀喔欢迎点赞关注异常处理异常处理是一项重要的特性它允许程序在运行时遇到错误条件时能够优雅地恢复或终止执行而不是简单地崩溃。C通过try、catch和throw三个关键字来实现异常处理机制。本文会详细介绍。基本概念定义异常是指在程序执行过程中发生的、不符合程序正常流程的事件。在C中异常通常是由于某些错误条件触发的如除以零、数组越界、内存分配失败等。关键字try用于标记可能会抛出异常的代码块称为保护代码。throw当检测到异常条件时使用throw关键字抛出一个异常。throw后面可以跟任意表达式它的类型决定了抛出的异常类型。catch用于捕获并处理异常。catch块紧跟在try块之后并指定了要捕获的异常类型。用法下面是一个简单的例子演示了如何使用try、throw和catch来处理除以零的异常代码块#includeiostreamusingnamespacestd;doubledivision(inta,intb){if(b0){throwDivision by zero!;// 抛出异常}return(a/b);}intmain(){intx50;inty0;doublez0;try{zdivision(x,y);// 可能抛出异常的代码coutzendl;}catch(constchar*msg){// 捕获并处理异常cerrmsgendl;}return0;}在这个例子中如果y为零division函数将抛出一个字符串异常该异常在main函数的catch块中被捕获并处理。一个try块可以跟随多个catch块来捕获多种不同类型的异常代码块#includeiostream#includestdexcept// 包含标准异常类usingnamespacestd;// 为了演示方便项目中不建议这样使用voidtestFunction(){throwruntime_error(Runtime error occurred!);}intmain(){try{testFunction();// 可能抛出异常的函数}catch(constlogic_errore){coutCaught a logic_error: e.what()endl;}catch(construntime_errore){coutCaught a runtime_error: e.what()endl;}catch(...){// 捕获所有其他类型的异常coutCaught an unknown exceptionendl;}return0;}testFunction抛出了一个runtime_error异常该异常在第二个catch块中被捕获并处理。最后一个catch块使用省略号…来捕获所有其他类型的异常。C标准异常类C标准库提供了一系列预定义的异常类这些类都继承自std::exception基类。使用标准异常类可以使代码更加清晰。层次结构如图标准异常类层次结构std::exception所有标准异常的基类。std::bad_alloc内存分配失败时抛出。std::bad_cast动态类型转换失败时抛出。std::bad_typeid使用 typeid 运算符失败时抛出。std::bad_exception在函数声明中使用了异常规格但抛出了未列出的异常时抛出 (C11已弃用)。std::logic_error逻辑错误异常基类包括:std::domain_error数学域错误如sqrt(-1)。std::invalid_argument无效参数错误。std::length_error超出允许长度的错误。std::out_of_range范围错误如访问vector的非法索引。std::runtime_error运行时错误异常基类包括:std::overflow_error上溢错误。std::range_error范围错误与 std::out_of_range 不同用于其他情况。std::underflow_error下溢错误。示例#includeiostream#includestdexceptusingnamespacestd;voidtestLogicError(){throwinvalid_argument(Invalid argument error!);}voidtestRuntimeError(){throwrange_error(range error!);}intmain(){try{testLogicError();// 抛出逻辑错误异常}catch(constlogic_errore){coutCaught a logic_error: e.what()endl;}try{testRuntimeError();// 抛出运行时错误异常}catch(construntime_errore){coutCaught a runtime_error: e.what()endl;}return0;}testLogicError 函数抛出了一个 invalid_argument 异常而 testRuntimeError 函数抛出了一个 range_error 异常。这两个异常分别在对应的 catch 块中被捕获并处理。自定义异常类虽然C标准库提供了丰富的异常类但在某些情况下开发者可能需要定义自己的异常类。我们可以通过继承 std::exception 基类并重载 what 方法来实现。示例#includeiostream#includeexception#includestringusingnamespacestd;classMyException:publicexception{public:MyException(conststringmessage):message_(message){}virtualconstchar*what()constnoexceptoverride{returnmessage_.c_str();}private:string message_;};voidtestCustomException(){throwMyException(Custom exception occurred!);}intmain(){try{testCustomException();// 抛出自定义异常}catch(constMyExceptione){coutCaught a MyException: e.what()endl;}catch(constexceptione){coutCaught an unknown exception: e.what()endl;}return0;}MyException 类继承自 std::exception 并重载了 what 方法。testCustomException 函数抛出了一个 MyException 异常该异常在 main 函数的 catch 块中被捕获并处理。注意这里还添加了一个捕获所有其他 std::exception 子类的 catch 块确保能够捕获所有未知异常。noexcept从C11开始推荐使用noexcept关键字来声明函数不抛出任何异常:voidfunc()noexcept;// 声明函数不抛出任何异常如果func函数在执行过程中抛出了异常程序会直接终止。noexcept关键字还可以用于提高性能因为编译器可以优化不抛出异常的函数调用。如果你看过 gcc 源码你会发现基本上通篇都是noexcept。noexcept会告诉编译器它修饰的函数不会产生异常exception这有利于编译器做更多的优化。C 的异常处理是在运行时检测的而不是在编译时检测为了运行时检测编译器应该会做些额外的操作如果能够通过noexcept明确的告诉编译器这个函数不会抛出异常编译器应该会做一些优化。验证函数是否 noexceptnoexcept还可以当作运算符它可以传入参数来验证某个函数是否是noexcept:voidmay_throw();voidno_throw()noexcept;intmain(){noexcept(may_throw());// falsenoexcept(no_throw());// true}什么时候使用 noexcept移动构造函数移动赋值函数建议使用noexcept修饰因为搭配标准库使用时noexcept作用巨大它可以优先移动而非拷贝推荐阅读 move_if_noexcept。而析构函数默认就是noexcept的不需要显式指定noexcept。在明确确认某个函数不会产生 exception 时可以使用noexcept。推荐看看这几个noexcept相关的文档:https://www.cnblogs.com/sword03/p/10020344.htmlhttps://en.cppreference.com/w/cpp/language/noexcept_spechttps://en.cppreference.com/w/cpp/language/noexcepthttps://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#Re-noexcepthttps://stackoverflow.com/questions/10787766/when-should-i-really-use-noexcepthttps://www.heise.de/blog/C-Core-Guidelines-Der-noexcept-Spezifier-und-Operator-4121657.html异常安全性异常安全性是指程序在遇到异常时仍然能够保持正确的状态不会出现资源泄露或者数据不一致等问题。因为C允许程序在执行过程中抛出异常这可能导致程序的控制流发生变化如果资源管理不当就可能出现资源泄露或程序崩溃等问题。异常跨函数传播示例有些场景下通过异常处理错误会更方便特别是跨函数传播的时候。场景描述图像处理流水线假设我们有一个图像处理系统分为三个独立编译的模块可能是不同团队开发的库底层库:ImageDecoder第三方库负责解码图像文件。中间层:ImageProcessor内部库负责图像增强。应用层:MainApp主程序处理用户请求。当底层库解码失败时异常会穿过中间层直达应用层处理。简易代码:// 底层库, 解码classImageDecoder{staticvoiddecode(){throwImageException(decode error);}};// 中间层, 图像处理voidImageProcess(){ImageDecoder::decode();}// 应用层intmain(){try{ImageProcess();}catch(ImageException e){xxx}catch(...){}return0;}Copy-and-Swap 惯用法与异常安全核心思想以拷贝构造 非抛出交换实现赋值操作利用 RAII 自动管理资源确保赋值要么完全成功要么原对象不变强异常安全保证。基本模式classBuffer{int*data_;size_t size_;// 私有辅助非抛出交换friendvoidswap(Buffera,Bufferb)noexcept{usingstd::swap;swap(a.data_,b.data_);swap(a.size_,b.size_);}public:Buffer(size_t n):data_(newint[n]),size_(n){}// 拷贝构造可能抛异常new但这是新对象没问题Buffer(constBufferother):data_(newint[other.size_]),size_(other.size_){std::copy(other.data_,other.data_other.size_,data_);}// 移动构造Buffer(Bufferother)noexcept:data_(other.data_),size_(other.size_){other.data_nullptr;other.size_0;}// 析构函数~Buffer(){delete[]data_;}// 拷贝赋值copy-and-swap 的核心Bufferoperator(constBufferother){// 1. 拷贝构造一个临时对象可能抛异常// 如果抛异常this 原封不动 → 强异常安全Buffertmp(other);// 2. 非抛出交换swap(*this,tmp);// 3. tmp 析构释放旧资源return*this;}// 移动赋值也可以复用 copy-and-swapBufferoperator(Bufferother)noexcept{swap(*this,other);return*this;}};异常安全流程拆解步骤操作若抛异常为什么安全1Buffer tmp(other)拷贝构造抛出 →tmp未构造完毕this不变拷贝在独立对象上进行2swap(*this, tmp)交换指针noexcept不会抛只用noexcept操作交换资源所有权3tmp析构析构不抛tmp现在持有旧资源自动释放关键要点swap必须noexcept— 这是整个惯用法的前提。如果swap可能抛就失去了强异常保证。参数传值 vs 传引用— 可以用传值代替传 const 引用 临时构造编译器可以优化// 简写传值方式copy-and-swap 的另一种写法Bufferoperator(Buffer other)noexcept{swap(*this,other);return*this;}这种写法同时支持拷贝赋值和移动赋值传右值时调用移动构造。自赋值安全— copy-and-swap 天然处理自赋值因为拷贝构造了独立副本交换后再释放旧资源。性能权衡每次赋值都分配新内存对于频繁赋值的场景可能不划算。如果性能敏感可以用copy-on-write或先判断资源是否足够复用。

相关新闻

15兆瓦海上风电仿真:IEA-15-240-RWT完整指南与实战应用

15兆瓦海上风电仿真:IEA-15-240-RWT完整指南与实战应用

15兆瓦海上风电仿真:IEA-15-240-RWT完整指南与实战应用 【免费下载链接】IEA-15-240-RWT 15MW reference wind turbine repository developed in conjunction with IEA Wind 项目地址: https://gitcode.com/gh_mirrors/ie/IEA-15-240-RWT 你是否想要快速掌握…

2026/10/13 16:03:08 阅读更多 →
长晶科技IC产品线解析与功率器件应用指南

长晶科技IC产品线解析与功率器件应用指南

1. 长晶科技IC产品线全景概览长晶科技作为国内半导体行业的重要参与者,其IC产品线覆盖了功率器件、模拟芯片、MCU等多个领域。我拆解过他们近三年发布的全部产品手册,发现其产品布局呈现明显的金字塔结构:基础型产品占比约60%,中端…

2026/10/6 4:53:41 阅读更多 →
F280015x ADC同步采样与高级应用实战:从SOC配置到系统级诊断

F280015x ADC同步采样与高级应用实战:从SOC配置到系统级诊断

1. 项目概述:深入理解F280015x的ADC同步采样与高级应用在电机控制、数字电源或者任何需要高精度实时反馈的嵌入式系统中,模数转换器(ADC)的性能往往是决定系统上限的关键瓶颈。我们常常面临这样的挑战:如何精确地同时捕…

2026/10/14 9:09:48 阅读更多 →

最新新闻

从柯西-施瓦兹不等式到匹配滤波:工程直觉与实战避坑指南

从柯西-施瓦兹不等式到匹配滤波:工程直觉与实战避坑指南

简介:这份资料围绕雷达信号处理中的匹配滤波技术展开,从柯西施瓦兹不等式这一数学基础切入,帮助读者理解匹配滤波器的理论来源与工程实现。内容适合具备一定信号处理与线性代数基础的高校学生、科研人员及雷达工程从业者,用于解决…

2026/10/15 0:48:41 阅读更多 →
手写C++ OBJ文件解析器:从格式到百万顶点优化与避坑指南

手写C++ OBJ文件解析器:从格式到百万顶点优化与避坑指南

简介:面向需要处理Wavefront OBJ三维模型格式的开发者和图形学入门者,这份资源提供一套基于Visual Studio 2013的obj解析C源码。源码实现完整的读取流程,覆盖顶点、纹理坐标、法线及多边形面这几类核心指令的解析,通过文件流逐行读…

2026/10/15 0:48:41 阅读更多 →
superpowers:用SKILL.md让Claude Code变身高效率AI开发助手

superpowers:用SKILL.md让Claude Code变身高效率AI开发助手

1. 先搞清楚:superpowers 到底是什么如果你平时用 Claude Code 比较多,可能早就遇到了一个让我当初很头疼的问题:底层模型确实聪明,但你在对话里让它"做个深度调研""写一套完整的单测""把这次的改动整理…

2026/10/15 0:48:41 阅读更多 →
用repomap给Claude Code一张代码地图,工具调用从52次降到3次

用repomap给Claude Code一张代码地图,工具调用从52次降到3次

1. 52 次工具调用到底是怎么花掉的我一直觉得 Claude Code 这类 CLI 编程助手最让人上头的点是“它真的会自己去翻代码库”,但最让人血压升高的点也是“它真的会自己去翻代码库”。刚上手那会儿,我在一个两万行左右的项目里让它排查登录流程的 token 过期…

2026/10/15 0:48:41 阅读更多 →
C#调用MATLAB绘图实战:COM自动化、句柄管理与嵌入式集成

C#调用MATLAB绘图实战:COM自动化、句柄管理与嵌入式集成

简介:本资源面向C#开发者与MATLAB混合编程实践者,解决在Windows平台下利用MATLAB强大图形引擎(尤其三维可视化)弥补C#原生绘图能力不足的核心问题。适用于科学计算、工程仿真、教学演示等需高质量数学图形的中高级开发场景。压缩包…

2026/10/15 0:48:41 阅读更多 →
在河北开发直播 APP,怎么挑选靠谱的软件公司?

在河北开发直播 APP,怎么挑选靠谱的软件公司?

河北不少创业者、企业都想搭建自己的直播 APP,带货直播、付费课程、娱乐打赏、私域直播等需求越来越多。但直播属于音视频高并发项目,技术门槛远高于普通商城和管理系统。很多本地软件公司只会做简单小程序,并不具备流媒体开发经验&#xff0…

2026/10/15 0:47:40 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/14 3:30:28 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/14 16:30:29 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 7:53:34 阅读更多 →