当前位置: 首页 > 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…...

React Native 开发环境搭建(全平台详解)

React Native 开发环境搭建&#xff08;全平台详解&#xff09; 在开始使用 React Native 开发移动应用之前&#xff0c;正确设置开发环境是至关重要的一步。本文将为你提供一份全面的指南&#xff0c;涵盖 macOS 和 Windows 平台的配置步骤&#xff0c;如何在 Android 和 iOS…...

连锁超市冷库节能解决方案:如何实现超市降本增效

在连锁超市冷库运营中&#xff0c;高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术&#xff0c;实现年省电费15%-60%&#xff0c;且不改动原有装备、安装快捷、…...

系统设计 --- MongoDB亿级数据查询优化策略

系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log&#xff0c;共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题&#xff0c;不能使用ELK只能使用…...

2025 后端自学UNIAPP【项目实战:旅游项目】6、我的收藏页面

代码框架视图 1、先添加一个获取收藏景点的列表请求 【在文件my_api.js文件中添加】 // 引入公共的请求封装 import http from ./my_http.js// 登录接口&#xff08;适配服务端返回 Token&#xff09; export const login async (code, avatar) > {const res await http…...

TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案

一、TRS收益互换的本质与业务逻辑 &#xff08;一&#xff09;概念解析 TRS&#xff08;Total Return Swap&#xff09;收益互换是一种金融衍生工具&#xff0c;指交易双方约定在未来一定期限内&#xff0c;基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...

深入解析C++中的extern关键字:跨文件共享变量与函数的终极指南

&#x1f680; C extern 关键字深度解析&#xff1a;跨文件编程的终极指南 &#x1f4c5; 更新时间&#xff1a;2025年6月5日 &#x1f3f7;️ 标签&#xff1a;C | extern关键字 | 多文件编程 | 链接与声明 | 现代C 文章目录 前言&#x1f525;一、extern 是什么&#xff1f;&…...

什么是Ansible Jinja2

理解 Ansible Jinja2 模板 Ansible 是一款功能强大的开源自动化工具&#xff0c;可让您无缝地管理和配置系统。Ansible 的一大亮点是它使用 Jinja2 模板&#xff0c;允许您根据变量数据动态生成文件、配置设置和脚本。本文将向您介绍 Ansible 中的 Jinja2 模板&#xff0c;并通…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

【数据分析】R版IntelliGenes用于生物标志物发现的可解释机器学习

禁止商业或二改转载&#xff0c;仅供自学使用&#xff0c;侵权必究&#xff0c;如需截取部分内容请后台联系作者! 文章目录 介绍流程步骤1. 输入数据2. 特征选择3. 模型训练4. I-Genes 评分计算5. 输出结果 IntelliGenesR 安装包1. 特征选择2. 模型训练和评估3. I-Genes 评分计…...

Chromium 136 编译指南 Windows篇:depot_tools 配置与源码获取(二)

引言 工欲善其事&#xff0c;必先利其器。在完成了 Visual Studio 2022 和 Windows SDK 的安装后&#xff0c;我们即将接触到 Chromium 开发生态中最核心的工具——depot_tools。这个由 Google 精心打造的工具集&#xff0c;就像是连接开发者与 Chromium 庞大代码库的智能桥梁…...