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

C++初学者指南-3.自定义类型(第一部分)-异常

C++初学者指南-3.自定义类型(第一部分)-异常

文章目录

  • C++初学者指南-3.自定义类型(第一部分)-异常
    • 简介
      • 什么是异常?
      • 第一个示例
      • 用途:报告违反规则的行为
      • 异常的替代方案
      • 标准库异常
      • 处理
    • 问题和保证
      • 资源泄露
      • 使用 RAII 避免内存泄漏!
      • 析构函数:不要让异常逃脱!
      • 异常保证
      • 无抛出异常保证:noexcept (C++11)

简介

什么是异常?

可以在调用层次结构中向上抛出的对象。

  • 抛出将控制权转移回当前函数的调用方
  • 它们可以通过try…catch块捕获/处理
  • 如果不处理,异常会向上传播,直到它们到达 main
  • 如果main中没有处理异常,将会调用std::terminate
  • std::terminate 的默认行为是中止程序
    在这里插入图片描述

第一个示例

异常的最初动机是报告构造函数未能正确初始化对象,即未能建立所需的类不变量(构造函数没有可用于错误报告的返回类型)。

#include <stdexcept>  // standard exception types
class Fraction {int numer_;int denom_;
public: explicit constexprFraction (int numerator, int denominator): numer_{numerator}, denom_{denominator}{if (denom_ == 0) throw std::invalid_argument{"denominator must not be zero"};}};
int main () {try {                              int d = 1;std::cin >> d;Fraction f {1,d};} catch (std::invalid_argument const& e) {// deal with / report error herestd::cerr << "error: " << e.what() << '\n';}}

运行上面代码

用途:报告违反规则的行为

  1. 前提条件违规
  • 前提条件 = 关于输入的期望(有效函数参数)
  • 违规示例: 越界容器索引/平方根为负数
  • 宽契约函数在使用其输入值之前执行前置条件检查
    在性能关键的代码中,如果传入的参数已经知道是有效的,那么人们不想支付输入有效性检查的成本,因此通常不会使用这些方法。
  1. 未能建立/保持不变量
  • 公共成员函数无法设置有效的成员值
  • 内存不足,向量vector增长失败
  1. 后置条件违规
  • 后置条件 = 关于输出的期望值(返回值)
  • 违规=函数未能产生有效的返回值或损坏全局状态
  • 例子:
    • 构造函数失败
    • 无法返回除以零的结果

异常的优点和缺点
优点1:将错误处理代码与业务逻辑分离
优点2:错误处理集中化(在调用链的更高层)
优点3:现在,当没有抛出异常时,性能影响可以忽略不计
缺点1:但是,抛出异常 时通常会影响性能
缺点2:由于额外的有效性检查而影响性能
缺点3:容易产生资源/内存泄漏(更多见下文)

异常的替代方案

输入值无效(违反前提条件)

  • 窄契约函数:在传递参数之前确保参数有效
  • 使用可以排除无效值的参数类型
  • 如今这是首选以获得更好的性能

未能建立/保留不变量

  • 错误状态/标志
  • 将对象设置为特殊的无效值/状态

无法返回有效值(违反后置条件)

  • 通过单独的输出参数(引用或指针)返回错误代码
  • 返回特殊的无效值
  • 使用特殊的词汇类型,可以包含有效结果,也可以什么都不包含,就像C++17的std::optional或Haskell的Maybe

标准库异常

异常是 C++ 标准库使用继承的少数地方之一:
所有标准异常类型都是std::exception的子类型。

std::exception↑ logic_error|  ↑ invalid_argument|  ↑ domain_error|  ↑ length_error|  ↑ out_of_range|  …↑ runtime_error↑ range_error↑ overflow_error↑ underflow_error…
try {throw std::domain_error{"Error Text"};
}
catch (std::invalid_argument const& e) {// 仅仅处理 'invalid_argument'异常}
// 捕捉其它所有异常
catch (std::exception const& e) {std::cout << e.what()// prints "Error Text"
}

一些标准库容器提供了宽契约函数,通过抛出异常来报告无效的输入值:

std::vector<int> v {0,1,2,3,4};
// 窄契约:不检查以获取最大性能
int a = v[6];     //  未定义行为
// 宽契约:检查是否超范围
int b = v.at(6);  // throws std::out_of_range

处理

重新抛出异常

try {   // potentially throwing code
} 
catch (std::exception const&) {  throw;  // re-throw exception(s)
}

捕获所有异常

try {                              // potentially throwing code
} 
catch (...) {  // handle failure
}

集中异常处理!

  • 如果同样的异常类型在许多不同的地方被抛出,可以避免代码重复。
  • 对于将异常转换为错误代码很有用
void handle_init_errors () {try { throw;  // re-throw! } catch (err::device_unreachable const& e) {} catch (err::bad_connection const& e) {} catch (err::bad_protocol const& e) {}
}
void initialize_server () {try {} catch (...) { handle_init_errors(); }
}
void initialize_clients () {try {} catch (...) { handle_init_errors(); }
}

问题和保证

资源泄露

几乎任何一段代码都可能抛出异常导致对 C++ 类型和库的设计产生重大影响。
如果与以下内容一起使用,则可能是资源/内存泄漏的潜在来源

  • 进行自己的内存管理的外部 C 库
  • (设计不佳)不使用 RAII 进行自动资源管理的 C++ 库
  • (设计不佳)在销毁时不清理资源的类型

示例:由于 C 风格的资源处理而导致的泄漏
即,两个单独的函数用于资源初始化(连接)和资源终止(断开连接)。

void add_to_database (database const& db, std::string_view filename) {DBHandle h = open_dabase_conncection(db);  auto f = open_file(filename);// 如果 "open_file"抛出异常,则链接不会断开!// do work…close_database_connection(h);// ↑ 如果"open_file"抛出了异常不会执行上面代码
}

使用 RAII 避免内存泄漏!

RAII 又是什么?

  • 构造函数:资源获取
  • 析构函数:资源释放/终结

如果抛出异常:

  • 局部作用域中的对象被销毁:被调用的析构函数
  • 使用 RAII:正确释放资源
class DBConnector {DBHandle handle_;
public:explicitDBConnector (Database& db): handle_{make_database_connection(db)} {}~DBConnector () { close_database_connection(handle_); }// 使connector不能复制:DBConnector (DBConnector const&) = delete;DBConnector& operator = (DBConnector const&) = delete;
};
void add_to_database (database const& db, std::string_view filename) {DBConnector(db);auto f = open_file(filename);// 如果 'open_file' 抛出异常 ⇒ 连接关闭!// do work normally…
} // 连接关闭了!

如果你需要使用一个(比如来自C语言的)库,这个库采用独立的初始化和资源释放函数,那么就编写一个RAII包装器。
通常,如果无法控制引用的外部资源,将包装器设为不可复制(删除复制构造函数和复制赋值运算符)也是有意义的。

析构函数:不要让异常逃脱!

… 否则资源可能会泄露!

class E {
public:~E () { // throwing code ⇒ BAD!}};
class A {// some members:G g;  F f;  E e;  D d;  C c;  B b;};

在这里插入图片描述
如果对象e析构时抛出异常的话会导致 f 和 g 对象的析构函数没有被调用。

在析构函数中: 捕获可能引发异常的代码!

class MyType {
public:~MyType () { …try {// y throwing code…} catch ( /* … */ ) {// handle exceptions…} …}
};

异常保证

如果引发异常:
不能保证
任何 C++ 代码都应该默认做出这个假设,除非它的文档另有说明:

  • 操作可能会失败
  • 资源可能泄露
  • 可能会破坏不变性(= 成员可能包含无效值)
  • 部分执行失败的操作可能会导致副作用(例如输出)
  • 异常可能会向外传播

基本保正

  • 不变量被保留,没有资源泄漏
  • 所有成员都将包含有效值
  • 执行失败操作的部分可能会导致一些副作用(例如,值可能已写入文件)

这是你最起码的目标!

强保证(提交或回滚语义)

  • 操作可能会失败,但不会产生明显的副作用
  • 所有成员都保留其原值

内存分配容器应该提供这一保证,即,如果在增长过程中内存分配失败,容器应保持有效和不变。

无抛出异常保证(最强)

  • 保证操作成功
  • 外部看不到任何异常(要么没有抛出异常,要么在内部被捕获了)
  • 使用 noexcept 关键字进行记录和强制执行

在高性能代码和资源受限的设备上,首选此功能。

无抛出异常保证:noexcept (C++11)

void foo () noexcept { … }
  • ‘foo’ 承诺永远不会抛出异常或让任何异常逃逸
  • 如果一个异常从一个 noexcept 函数中逃逸了,程序会被终止

好好想想,你能不能遵守不抛出异常的承诺!

  • noexcept是函数的接口的一部分(甚至是自C++17函数类型的一部分)
  • 稍后将不抛出异常的函数更改为抛出异常的函数可能会破坏那些依赖不必处理异常的调用代码

有条件noexcept

A noexcept( expression )如果表示式为真则声明A不抛出异常
A noexcept( noexcept( B ) )如果B为不抛出异常则声明A也不抛出异常

默认情况下为 noexcept(true)

都是隐式声明的特殊成员

  • 默认构造函数
  • 析构函数
  • 复制构造函数, 移动构造函数
  • 复制赋值运算符、移动赋值运算符
  • 继承的构造函数
  • 用户定义的析构函数

以上这些都是默认noexcept(true)

除非

  • 他们需要调用 noexcept(false) 的函数
  • 明确的声明另有说明

附上原文地址
如果文章对您有用,请随手点个赞,谢谢!^_^

相关文章:

C++初学者指南-3.自定义类型(第一部分)-异常

C初学者指南-3.自定义类型(第一部分)-异常 文章目录 C初学者指南-3.自定义类型(第一部分)-异常简介什么是异常&#xff1f;第一个示例用途:报告违反规则的行为异常的替代方案标准库异常处理 问题和保证资源泄露使用 RAII 避免内存泄漏&#xff01;析构函数&#xff1a;不要让异…...

学会python——用python编写一个电子时钟(python实例十七)

目录 1.认识Python 2.环境与工具 2.1 python环境 2.2 Visual Studio Code编译 3.电子时钟程序 3.1 代码构思 3.2代码实例 3.3运行结果 4.总结 1.认识Python Python 是一个高层次的结合了解释性、编译性、互动性和面向对象的脚本语言。 Python 的设计具有很强的可读性…...

elementui中@click短时间内多次触发,@click重复点击,做不允许重复点击处理

click快速点击&#xff0c;发生多次触发 2.代码示例&#xff1a; //html<el-button :loading"submitLoading" type"primary" click"submitForm">确 定</el-button>data() {return {submitLoading:false,}}//方法/** 提交按钮 */sub…...

助力游戏实现应用内运营闭环,融云游戏社交方案升级!

通信能力在所有应用场景都是必备组件&#xff0c;这源于社交属性带给应用的增长神话。 在游戏场景&#xff0c;玩家从少数核心向大众用户泛化扩展的过程&#xff0c;就是游戏深度融合社交能力的过程。 从单机到联机&#xff0c;游戏乐趣的升级 1996 年&#xff0c;游戏界顶流…...

守护创新之魂:源代码防泄漏的终极策略

在信息化快速发展的今天&#xff0c;企业的核心机密数据&#xff0c;尤其是源代码&#xff0c;成为了企业竞争力的关键所在。然而&#xff0c;源代码的泄露风险也随之增加&#xff0c;给企业的安全和发展带来了巨大威胁。在这样的背景下&#xff0c;SDC沙盒作为一种创新的源代码…...

Halcon 基于分水岭的目标分割

一 分水岭 1 分水岭介绍 传统的分水岭分割方法&#xff0c;是一种基于拓扑理论的数学形态学的分割方法&#xff0c;其基本思想是把图像看作是地质学上的拓扑地貌&#xff0c;图像中每一像素的灰度值表示该点的海拔高度&#xff0c;每一个局部极小值及其周边区域称为集水盆地&…...

PHP 面向对象编程(OOP)入门指南

面向对象编程&#xff08;Object-Oriented Programming&#xff0c;简称OOP&#xff09;是一种编程范式&#xff0c;通过使用对象来设计和组织代码。PHP作为一种广泛使用的服务器端脚本语言&#xff0c;支持面向对象编程。本文将介绍PHP面向对象编程的基本概念和用法&#xff0…...

Django学习第三天

python manage.py runserver 使用以上的命令启动项目 实现新建用户数据功能 views.py文件代码 from django.shortcuts import render, redirect from app01 import models# Create your views here. def depart_list(request):""" 部门列表 ""&qu…...

Vue3实现点击按钮实现文字变色

1.动态样式实现 1.1核心代码解释&#xff1a; class"power-station-perspective-item-text"&#xff1a; 为这个 span 元素添加了一个 CSS 类&#xff0c;以便对其样式进行定义。 click"clickItem(item.id)"&#xff1a; 这是一个 Vue 事件绑定。当用户点…...

深入理解Vue生命周期钩子函数

深入理解Vue生命周期钩子函数 Vue.js 是一款流行的前端框架&#xff0c;通过其强大的响应式数据绑定和组件化的开发方式&#xff0c;使得前端开发变得更加简单和高效。在Vue应用中&#xff0c;每个组件都有其生命周期&#xff0c;这些生命周期钩子函数允许开发者在不同阶段执行…...

Linux-gdb

目录 1.-g 生成含有debug信息的可执行文件 2.gdb开始以及gdb中的常用执行指令 3.断点的本质用法 4.快速跳出函数体 5.其他 1.-g 生成含有debug信息的可执行文件 2.gdb开始以及gdb中的常用执行指令 3.断点的本质用法 断点的本质是帮助我们缩小出问题的范围 比如&#xff0c;…...

Oracle分析表和索引(analyze)

分析表 analyze table tablename compute statistics; 分析索引 analyze index indexname compute statistics; 该语句生成的统计信息会更新user_tables这个视图的统计信息,分析的结果被Oracle用于基于成本的优化生成更好的查询计划 对于使用CBO(Cost-Base Optimization)很有好…...

MyBatis踩坑记录-多表关联字段相同,字段数据覆盖问题

MyBatis踩坑记录-多表关联字段相同&#xff0c;字段数据覆盖问题 1. 背景描述2. 实体记录3. 错误映射3.1 造成的影响 4. 解决办法4.1 修改映射文件 5. 修复后的效果5.1 返回的数据5.2 正确展示 7. end ~ 1. 背景描述 现有一下业务&#xff0c;单个任务下可能会有多个子任务&am…...

昇思25天学习打卡营第6天|数据变换 Transforms

学习目标&#xff1a;熟练掌握数据变换操作 熟悉mindspore.dataset.transforms接口 实践掌握常用变换 昇思大模型平台学习心得记录&#xff1a; 一、关于mindspore.dataset.transforms 1.1 变换 mindspore.dataset.transforms.Compose将多个数据增强操作组合使用。 mindspo…...

在线JSON可视化工具--改进

先前发布了JSON格式化可视化在线工具&#xff0c;提供图形化界面显示结构关系功能&#xff0c;并提供JSON快速格式化、JSON压缩、快捷复制、下载导出、对存在语法错误的地方能明确显示&#xff0c;而且还支持全屏&#xff0c;极大扩大视野区域。 在线JSON格式化可视化工具 但…...

探讨命令模式及其应用

目录 命令模式命令模式结构命令模式适用场景命令模式优缺点练手题目题目描述输入描述输出描述题解 命令模式 命令模式是一种行为设计模式&#xff0c; 它可将请求转换为一个包含与请求相关的所有信息的独立对象。 该转换让你能根据不同的请求将方法参数化、 延迟请求执行或将其…...

1、音视频解封装流程---解复用

对于一个视频文件(mp4格式/flv格式)&#xff0c;audio_pkt或者video_pkt是其最基本的数据单元&#xff0c;即视频文件是由独立的视频编码包或者音频编码包组成的。 解复用就是从视频文件中把视频包/音频包单独读取出来保存成独立文件&#xff0c;那么如何得知packet是视频包还是…...

centos7升级gcc到7.3.0

1、下载gcc-7.3.0源码 wget ftp.gnu.org/gnu/gcc/gcc-7.3.0/gcc-7.3.0.tar.gz 2、解压gcc-7.3.0 tar -xvf gcc-7.3.0.tar.gz3、安装依赖 cd gcc-7.3.0 ./contrib/download_prerequisites ./contrib/download_prerequisites会下载对应的依赖包&#xff0c;如果下载不了的话&a…...

系统运维面试题总结(网络基础类)

系统运维面试题总结&#xff08;网络基础类&#xff09; 网络基础类第七层&#xff1a;应用层第六层&#xff1a;表示层第五层&#xff1a;会话层第四层&#xff1a;传输层第三层&#xff1a;网络层第二层&#xff1a;数据链路层第一层&#xff1a;物理层 类似面试题1、TCP/IP四…...

PO模式登录测试

项目实践 登陆项目测试 get_driver import page from selenium import webdriverclass GetDriver:driver Noneclassmethoddef get_driver(cls):if cls.driver is None:cls.driver webdriver.Edge()cls.driver.maximize_window()cls.driver.get(page.url)return cls.drivercl…...

HFSS仿真结果怎么看?以T型波导为例,读懂S参数与电场动态图

HFSS仿真结果深度解析&#xff1a;从S参数到电场动态图的实战指南当你第一次在HFSS中完成T型波导仿真后&#xff0c;面对满屏的曲线和彩色云图&#xff0c;是否感到既兴奋又困惑&#xff1f;那些起伏的S参数曲线究竟告诉你什么信息&#xff1f;电场图中跳跃的颜色又代表怎样的物…...

echarts中heatmap鼠标滚动禁用缩放,向下滚动

配置如下效果如下...

2026上半年数据库系统工程师(软考)上午题回忆与解析(非标答版)

本文为考后回忆整理&#xff0c;非官方标准答案&#xff0c;旨在为考后对答案及下半年备考的同学提供参考。题目顺序和表述可能与原卷有出入&#xff0c;欢迎在评论区指正、补充。&#x1f4ca; 整体考情分析 刚结束的2026年上半年数据库系统工程师考试&#xff0c;上午题的风格…...

潮州东方轻奢风全屋高定找哪家

开篇引言根据《2026年中国全屋定制行业发展报告》&#xff0c;潮州市全屋定制市场规模同比增长38%&#xff0c;其中全屋高端定制细分市场同比增长52%。目前&#xff0c;潮州市家庭全屋定制需求占比72%&#xff0c;高端定制需求占比45%。为了帮助潮州市消费者选择合规、靠谱、差…...

终极Chrome画中画扩展:如何在浏览器中实现高效视频多任务处理

终极Chrome画中画扩展&#xff1a;如何在浏览器中实现高效视频多任务处理 【免费下载链接】picture-in-picture-chrome-extension 项目地址: https://gitcode.com/gh_mirrors/pi/picture-in-picture-chrome-extension 想要在浏览网页、处理文档的同时继续观看视频内容吗…...

别再死记公式了!用Python手写一个卷积层,彻底搞懂CNN里的‘卷’是怎么算的

用Python手写卷积层&#xff1a;从零理解CNN的"卷"运算 当你第一次看到卷积神经网络(CNN)的数学公式时&#xff0c;那些复杂的符号和下标是否让你望而却步&#xff1f;作为计算机视觉领域的基石&#xff0c;CNN的核心在于理解卷积运算的本质。本文将带你用NumPy从零实…...

别再纠结了!给激光焊接新手讲透单模和多模激光到底怎么选(附M²因子解读)

激光焊接设备选型指南&#xff1a;单模与多模激光的实战抉择 当你第一次站在激光焊接设备采购的十字路口&#xff0c;面对"单模"和"多模"这两个专业术语时&#xff0c;那种迷茫感我深有体会。五年前&#xff0c;我作为产线技术负责人&#xff0c;需要为汽车…...

Facebook登录协议逆向解析:appsecret_proof与e2e加密机制

1. 这不是“爬虫教程”&#xff0c;而是一次对现代Web身份协议的解剖实验你有没有试过&#xff0c;在调试一个Facebook登录集成时&#xff0c;浏览器Network面板里突然冒出一串带sig、access_token、e2e、c_user的请求&#xff0c;参数长度动辄上千字符&#xff0c;加密方式五花…...

结肠“瑞士卷”制片法

在肠道病理研究中&#xff0c;如何完整保留小鼠结肠的全层结构、同时避免人为损伤&#xff0c;一直是实验操作的难点。本文分享一套改良版“瑞士卷”制片技术&#xff0c;无需剖开肠管、无需机械顶压&#xff0c;即可获得高质量的全结肠切片&#xff0c;特别适合炎症、隐窝异常…...

内存申请和使用的场景分析(以AP->kernal->ISP为例)

在 ISP&#xff08;Image Signal Processor&#xff09;系统中&#xff0c;AP 与 ISP 之间的内存交互本质上是一个**“AP 申请可 DMA 访问的共享内存 → 内核建立映射 → 硬件寻址读写 → 同步与回收”**的过程。下面按数据流分层详细拆解。一、ISP 内存需求的特殊性 与普通应用…...