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';}…
}
运行上面代码
用途:报告违反规则的行为
- 前提条件违规
- 前提条件 = 关于输入的期望(有效函数参数)
- 违规示例: 越界容器索引/平方根为负数
- 宽契约函数在使用其输入值之前执行前置条件检查
在性能关键的代码中,如果传入的参数已经知道是有效的,那么人们不想支付输入有效性检查的成本,因此通常不会使用这些方法。
- 未能建立/保持不变量
- 公共成员函数无法设置有效的成员值
- 内存不足,向量vector增长失败
- 后置条件违规
- 后置条件 = 关于输出的期望值(返回值)
- 违规=函数未能产生有效的返回值或损坏全局状态
- 例子:
- 构造函数失败
- 无法返回除以零的结果
异常的优点和缺点
优点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.自定义类型(第一部分)-异常简介什么是异常?第一个示例用途:报告违反规则的行为异常的替代方案标准库异常处理 问题和保证资源泄露使用 RAII 避免内存泄漏!析构函数:不要让异…...

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

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

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

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

PHP 面向对象编程(OOP)入门指南
面向对象编程(Object-Oriented Programming,简称OOP)是一种编程范式,通过使用对象来设计和组织代码。PHP作为一种广泛使用的服务器端脚本语言,支持面向对象编程。本文将介绍PHP面向对象编程的基本概念和用法࿰…...

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核心代码解释: class"power-station-perspective-item-text": 为这个 span 元素添加了一个 CSS 类,以便对其样式进行定义。 click"clickItem(item.id)": 这是一个 Vue 事件绑定。当用户点…...

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

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

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

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

昇思25天学习打卡营第6天|数据变换 Transforms
学习目标:熟练掌握数据变换操作 熟悉mindspore.dataset.transforms接口 实践掌握常用变换 昇思大模型平台学习心得记录: 一、关于mindspore.dataset.transforms 1.1 变换 mindspore.dataset.transforms.Compose将多个数据增强操作组合使用。 mindspo…...

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

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

1、音视频解封装流程---解复用
对于一个视频文件(mp4格式/flv格式),audio_pkt或者video_pkt是其最基本的数据单元,即视频文件是由独立的视频编码包或者音频编码包组成的。 解复用就是从视频文件中把视频包/音频包单独读取出来保存成独立文件,那么如何得知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会下载对应的依赖包,如果下载不了的话&a…...

系统运维面试题总结(网络基础类)
系统运维面试题总结(网络基础类) 网络基础类第七层:应用层第六层:表示层第五层:会话层第四层:传输层第三层:网络层第二层:数据链路层第一层:物理层 类似面试题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…...

X86 +PC104+支持WinCE5.0,WinCE6.0,DOS,WinXP, QNX等操作系统,工业控制数据采集核心模块板卡定制
CPU 模块 是一款基于RDC 3306的SOM Express模块。RDC 3306这款X86架构的CPU是一款性能高、稳定性强的处理器。 它是一款灵活精巧的主板(尺寸为91.8mm68.6mm),可以灵活的运用于用户的底板,节约开发成本。模块的接插件使用插针形式…...

视频监控汇聚和融合平台的特点、功能、接入方式、应用场景
目录 一、产品概述 二、主要特点 1、多协议支持 2、高度集成与兼容性 3、高性能与可扩展性 4、智能化分析 5、安全可靠 三、功能概述 1. 视频接入与汇聚 2. 视频存储与回放 3. 实时监控与预警 4. 信息共享与联动 5. 远程管理与控制 四、接入方式 1、直接接入 2…...

实习总结 --- 其他业务
一. 回归测试:回归测试与测新是对应的,当需求准入交付测试的时候首先要进行的就是测新,也就是对新功能对测试,一般是在sim环境下测试的;当测新通过后才会进行回归测试,回归测试的目的是为了保证老功能的正确…...

2024年上半年典型网络攻击事件汇总
文章目录 前言一、Ivanti VPN 的0 Day攻击(2024年1月)二、微软公司高管账户泄露攻击(2024年1月)三、Change Healthcare网络攻击(2024年2月)四、ConnectWise ScreenConnect漏洞利用攻击(2024年2月)五、XZ Utils软件供应链攻击(2024年3月)六、AT&T数据泄露攻击(20…...

Ozon、美客多补单测评黑科技:打造无懈可击的自养号补单环境
不管哪个跨境平台的风控都会做升级,相对的补单技术也需要进行相应的做升级,风控升级后,自己养号补单需要注意以下技术问题,以确保补单的稳定性和安全性: 一、物理环境 1. 硬件参数伪装:平台已经开始通过I…...

ES报错:解决too_many_clauses: maxClauseCount is set to 1024 报错问题
解决too_many_clauses: maxClauseCount is set to 1024 报错问题 问题场景报错信息问题分析解决1. 优化查询2. 增加maxClauseCount3. 改用其他查询类型修改后的查询示例 问题场景 查询语句:查询clcNo分类号包含分类O的所有文档 {"match_phrase_prefix":…...

完全指南:在Linux上安装和精通Conda
前言 Conda是一个强大的包管理和环境管理工具,特别适用于数据科学和机器学习项目。本文将详细指导你在Linux系统上安装、配置和充分利用Conda的方法。 步骤一:下载和安装Conda 下载安装包: wget https://repo.anaconda.com/miniconda/Minic…...

# linux 系统中,使用 “ ll “ 命令报错 “ bash ll command not found “ 解决方法:
linux 系统中,使用 " ll " 命令报错 " bash ll command not found " 解决方法: 一、错误描述: 报错原因: 1、这个错误表明你尝试在 bash shell 中执行 ll 命令,但是系统找不到这个命令。ll 通常…...

吴恩达深度学习笔记:机器学习策略(2)(ML Strategy (2)) 2.3-2.4
目录 第三门课 结构化机器学习项目(Structuring Machine Learning Projects)第二周:机器学习策略(2)(ML Strategy (2))2.3 快速搭建你的第一个系统,并进行迭代(Build your first system quickly…...

【软件测试】快速定位bug,编写测试用例
作为一名测试人员如果连常见的系统问题都不知道如何分析,频繁将前端人员问题指派给后端人员,后端人员问题指派给前端人员,那么在团队里你在开发中的地位显而易见 ,口碑、升值、加薪那应该是你遥不可及的梦 但是作为测试人员来说&…...