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/7/23 9:10:27 阅读更多 →
长晶科技IC产品线解析与功率器件应用指南

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

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

2026/7/24 1:37:49 阅读更多 →
F280015x ADC同步采样与高级应用实战:从SOC配置到系统级诊断

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

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

2026/7/25 0:31:18 阅读更多 →

最新新闻

终极指南:YuukiPS Launcher-PC多游戏启动工具从入门到精通

终极指南:YuukiPS Launcher-PC多游戏启动工具从入门到精通

终极指南:YuukiPS Launcher-PC多游戏启动工具从入门到精通 【免费下载链接】Launcher-PC 项目地址: https://gitcode.com/gh_mirrors/la/Launcher-PC YuukiPS Launcher-PC是一款专为动漫游戏爱好者设计的C#跨平台启动工具,提供游戏检测、版本管理…

2026/7/25 0:33:50 阅读更多 →
打工人的救星!我用Doubao-Seed-Evolving开发了个“做饭助手“,从外卖党变身厨房小能手

打工人的救星!我用Doubao-Seed-Evolving开发了个“做饭助手“,从外卖党变身厨房小能手

一、打工人的真实困境 每天下班的你: 18:30 下班路上 大脑:今晚又吃外卖吗? 我:第5天了,真吃腻了…… 大脑:那自己做? 我:不会做啊,做什么? 大脑&#xff1…

2026/7/25 0:33:50 阅读更多 →
古风文案创作 —— 鸿蒙AI智能助手开发全流程解析

古风文案创作 —— 鸿蒙AI智能助手开发全流程解析

🎐 古风文案创作 —— 鸿蒙AI智能助手开发全流程解析分类: 创意写作 | 应用编号: App50 | 平台: HarmonyOS NEXT 关键词: 鸿蒙、鸿蒙PC、鸿蒙Flutter框架、AI应用、ArkTS、HarmonyOS NEXT 摘要: 本文基于古…

2026/7/25 0:33:50 阅读更多 →
系统自动化测试及其应用

系统自动化测试及其应用

一、项目概述 我曾参与某大型电商平台订单管理系统的研发与测试工作,在该项目中担任测试架构师,负责系统测试的整体规划与自动化测试体系的搭建。该系统是一个典型的分布式微服务架构,涵盖订单创建、支付处理、库存扣减、物流跟踪、售后管理…

2026/7/25 0:33:50 阅读更多 →
2026年商用清洁机器人品牌推荐榜:五大品牌全场景实力解析

2026年商用清洁机器人品牌推荐榜:五大品牌全场景实力解析

商用清洁机器人正在从“买一台设备试试看”,进入按场景、面积和运营目标成套部署的新阶段。 国际机器人联合会(IFR)发布的《World Robotics 2025—Industrial Robots》显示,2024年全球工业机器人新安装量达到542076台&#xff0c…

2026/7/25 0:33:50 阅读更多 →
绝区零自动化助手:5分钟配置指南,开启智能游戏体验

绝区零自动化助手:5分钟配置指南,开启智能游戏体验

绝区零自动化助手:5分钟配置指南,开启智能游戏体验 【免费下载链接】ZenlessZoneZero-OneDragon 绝区零 一条龙 | 全自动 | 自动闪避 | 自动每日 | 自动空洞 | 支持手柄 项目地址: https://gitcode.com/gh_mirrors/ze/ZenlessZoneZero-OneDragon …

2026/7/25 0:31:50 阅读更多 →

日新闻

突破文档下载限制:kill-doc让你看到的都能保存

突破文档下载限制:kill-doc让你看到的都能保存

突破文档下载限制:kill-doc让你看到的都能保存 【免费下载链接】kill-doc 看到经常有小伙伴们需要下载一些免费文档,但是相关网站浏览体验不好各种广告,各种登录验证,需要很多步骤才能下载文档,该脚本就是为了解决您的…

2026/7/25 0:00:35 阅读更多 →
C++ string类模拟实现:从深拷贝到内存管理的完整指南

C++ string类模拟实现:从深拷贝到内存管理的完整指南

1. 项目概述:为什么我们要“手撕”string类?在C的学习道路上,尤其是从C语言过渡到C的“初阶”阶段,string类绝对是一个绕不开的核心。标准库里的std::string用起来太方便了,、find、substr,几个操作符和函数…

2026/7/25 0:00:35 阅读更多 →
三角洲寻宝鼠工具:高效文件搜索与资源管理实战指南

三角洲寻宝鼠工具:高效文件搜索与资源管理实战指南

1. 先搞清楚“三角洲寻宝鼠”到底是什么工具从名称来看,“三角洲寻宝鼠”更像是一个资源查找或文件检索类工具,而不是游戏或娱乐软件。这类工具的核心价值在于帮助用户快速定位特定资源,比如文档、图片、压缩包或特定格式的文件。如果你经常需…

2026/7/25 0:00:35 阅读更多 →

周新闻

Go语言静态资源打包方案对比与实践指南

Go语言静态资源打包方案对比与实践指南

1. 项目背景与核心需求在Go语言开发中,我们经常需要处理静态资源文件的打包问题。无论是Web应用的模板文件、前端资源,还是配置文件、证书等,都需要随程序一起分发。传统做法是将这些文件与编译后的二进制文件放在同一目录下,但这…

2026/7/24 3:59:20 阅读更多 →
Go语言实现高性能LDAP认证服务的架构与实践

Go语言实现高性能LDAP认证服务的架构与实践

1. 项目背景与核心价值LDAP(轻量级目录访问协议)作为企业级身份认证的黄金标准,已经服务了超过80%的财富500强公司。我在金融科技领域实施统一认证体系时,发现传统Java方案存在启动慢、内存占用高等痛点。而Go语言凭借其协程并发模…

2026/7/24 1:23:39 阅读更多 →
【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

【AI面试官实战指南】:用ChatGPT模拟10类高频技术岗面试,3天提升应答精准度92%

更多请点击: https://intelliparadigm.com 第一章:AI面试官实战指南的核心价值与适用场景 AI面试官并非替代人类HR的“黑箱工具”,而是以可解释、可审计、可迭代的方式,赋能招聘全链路的关键基础设施。其核心价值在于将主观经验沉…

2026/7/24 18:52:18 阅读更多 →

月新闻