C++(17):异常处理

异常处理机制允许程序中独立开发的部分能够在运行时就出现的问题进行通信并做出相应的处理。

异常使得能够将问题的检测与解决过程分离开来:程序的一部分负责检测问题的出现,然后解决该问题的任务传递给程序的另一部分。检测环节无须知道问题处理模块的所有细节,反之亦然。

抛出异常

C++ 中,通过抛出(throwing)一条表达式来引发(raised)一个异常。
被抛出的表达式的类型以及当前的调用链共同决定了哪段处理代码(handler)将被用来处理该异常。
被选中的处理代码是在调用链中与抛出对象类型匹配的最近的处理代码。
其中,根据抛出对象的类型和内容,程序的异常抛出部分将会告知异常处理部分到底发生了什么错误。

当执行一个 throw 时,跟在 throw 后面的语句将不再被执行。相反,程序的控制权从throw 转移到与之匹配的 catch 模块。
catch 可能是同一个函数中的局部 catch,也可能位于直接或间接调用了发生异常的函数的另一个函数中。控制权从一处转移到另一处,这有两个重要的含义:
1. 沿着调用链的函数可能会提早退出。
2. 一旦程序开始执行异常处理代码,则沿着调用链创建的对象将被销毁。
因为跟在 throw 后面的语句将不再被执行,所以 throw 语句的用法有点类似于 return 语句:它通常作为条件语句的一部分或者作为某个函数的最后(或者唯一)一条语句。

栈展开

当抛出一个异常后,程序暂停当前函数的执行过程并立即开始寻找与异常匹配的 catch 子句。当throw出现在一个 try 语句块(try block)内时,检查与该try块关联的catch子句。
如果找到了匹配的catch,就使用该catch处理异常。
如果这一步没找到匹配的catch且该try语句嵌套在其他try块中,则继续检查与外层try匹配的catch子句。
如果还是找不到匹配的 catch,则退出当前的函数,在调用当前函数的外层函数中继续寻找。
如果对抛出异常的函数的调用语句位于一个try语句块内,则检查与该try块关联的catch子句。如果找到了匹配的catch,就使用该catch处理异常。否则,如果该try语句嵌套在其他try块中,则继续检查与外层try匹配的catch子句。如果仍然没有找到匹配的catch,则退出当前这个主调函数,继续在调用了刚刚退出的这个函数的其他函数中寻找,以此类推。

上述过程被称为栈展开过程。沿着嵌套函数的调用链不断查找,直到找到与异常匹配的catch子句为止;或者一直没找到匹配的 catch 退出主函数后查找过程中值。

假设找到了一个匹配的 catch子句,则程序进入该子句并执行其中的代码。当执行完这个catch子句后,找到与try块关联的最后一个 catch子句之后的点,并从这里继续执行。
如果没找到匹配的 catch子句,程序将退出。

因为异常通常被认为是妨碍程序正常执行的事件,所以一旦引发了某个异常,就不能对它置之不理。当找不到匹配的 catch时,程序将调用标准库函数 terminate,顾名思义,terminate负责终止程序的执行过程。

一个异常如果没有被捕获,则它将终止当前的程序。

栈展开过程中对象被自动销毁

如果栈展开过程中退出了某个块,那么在这个块中创建的局部对象将会被销毁。

析构函数与异常

析构函数总是会被执行的,但是函数中负责释放资源的代码却可能被跳过。
栈展开的过程中,类类型的局部对象的析构函数会被执行以销毁该对象。析构函数不应该抛出异常。如果析构函数需要执行某个可能抛出异常的操作,该操作应该放在 try 语句块中并在析构函数内部得到处理。
原因:当析构函数抛出了异常,又没有在析构函数内部完成处理的话,析构函数将会提早退出,导致没有完成对象的销毁工作。

异常对象

异常对象是一种特殊的对象,编译器使用异常抛出表达式来对异常对象进行拷贝初始化。因此,throw语句中的表达式必须拥有完全类型。
而且如果该表达式是类类型的话,则相应的类必须含有一个可访问的析构函数和一个可访问的考贝或移动构造函数。如果该表达式是数组类型或函数类型,则表达式将被转换成与之对应的指针类型。

异常对象位于编译器管理的空间中,编译器确保无论调用的是哪个 catch 子句,都能访问该空间。当异常处理结束后,该异常对象被销毁。

抛出指针要求在任何对应的处理代码存在的地方,指针所指的对象都必须存在。

捕获异常

catch子句中的异常声明看起来像是只包含一个形参的函数形参列表。像在形参列表中一样,如果catch无须访问抛出的表达式的话,则可以忽略捕获形参的名字。

声明的类型决定了处理代码所能捕获的异常类型。这个类型必须是完全类型,它可以是左值引用,但不能是右值引用。

当进入一个catch语句后,通过异常对象初始化异常声明中的参数:
如果catch 的参数类型是非引用类型,则该参数是异常对象的一个副本,在catch语句内改变该参数实际上改变的是局部副本而非异常对象本身;
相反,如果参数是引用类型,则和其他引用参数一样,该参数是异常对象的一个别名,此时改变参数也就是改变异常对象。

如果 catch 的参数是基类类型,则可以使用派生类类型的异常对象对其进行初始化。此时,如果 catch 的参数是非引用类型,则异常对象会被切掉一部分。如果 catch 的参数是基类的引用,该参数将以常规方式绑定到异常对象上。

异常声明的静态类型将决定 catch 语句所能执行的操作。

如果 catch 接受的异常与某个继承体系有关,最好将该 catch 的参数定义为引用类型。

查找匹配的处理代码

在搜寻catch语句的过程中,最终找到的catch未必是异常的最佳匹配。相反,挑选出来的应该是第一个与异常匹配的catch语句,越是专门的catch越应该置于整个catch 列表的前端。

当程序使用具有继承关系的多个异常时必须对catch语句的顺序进行组织和管理,使得派生类异常的处理代码出现在基类异常的处理代码之前。
原因: catch语句是按照其出现的顺序逐一进行匹配的

与实参和形参的匹配规则相比,异常和 catch 异常声明的匹配规则更严格一些,可以进行以下三种转换:
1. 允许从非常量向常量的类型转换;
2. 允许从派生类向基类的类型转换;
3. 数组被转换为指向数组(元素)类型的指针,函数被转换成指向该函数类型的指针。

重新抛出

有时,一个单独的catch语句不能完整地处理某个异常。在执行了某些校正操作之后,当前的catch可能会决定由调用链更上一层的函数接着处理异常。
一条catch语句通过重新抛出(rethrowing)的操作将异常传递给另外一个catch语句。这里的重新抛出仍然是一条throw语句,只不过不包含任何表达式: throw;
空的throw语句只能出现在 catch语句或catch 语句直接或间接调用的函数之内。如果在处理代码之外的区域遇到了空throw语句,编译器将调用terminate

捕获所有异常的处理代码

使用省略号作为异常声明,可以一次性捕获所有异常。形如catch(...)

//catch(...)通常与重新抛出语句一起使用,其中 catch 执行当前局部能完成的工作,随后重新抛出异常
void manip(){try {//这里的操作将引发并抛出一个异常}catch(...){//处理异常的某些特殊操作throw ;}
}

如果catch(...)与其他几个 catch语句一起出现,则 catch(...)必须在最后的位置。出现在捕获所有异常语句后面的catch语句将永远不会被匹配。

函数 try 语句块与构造函数

通常情况下,程序执行的任何时刻都可能发生异常,特别是异常可能发生在处理构造函数初始值的过程中。
构造函数在进入其函数体之前首先执行初始值列表。因为在初始值列表抛出异常时构造函数体内的try语句块还未生效,所以构造函数体内的catch语句无法处理构造函数初始值列表抛出的异常。

处理构造函数初始值异常的唯一方法就是将构造函数写成函数 try 语句块。

template <typenameT>
Blob<T>::Blob(std::initializer_list<T> il)
try:data (std: :make shared<std: :vector<T>>(il)){/*空函数体*/
}catch (const std::bad_alloc &e){ handle_out_of_memory(e);}

注意:关键字try出现在表示构造函数初始值列表的冒号以及表示构造函数体的花括号之前。与这个try关联的catch 既能处理构造函数体抛出的异常,也能处理成员初始化列表抛出的异常。

noexcept 异常说明

noexcept 异常说明可以指定某个函数不会抛出异常。其形式是 关键字 noexcept 紧跟在函数的参数列表后面:

void recoup(int) noexcept;//不会抛出异常

编译器并不会在编译时检查noexcept 说明。实际上,如果一个函数在说明了noexcept的同时又含有throw语句或者调用了可能抛出异常的其他函数,编译器将顺利编译通过。并不会因为这种违反异常说明的情况而报错。

异常说明的实参

noexcept 说明符接受一个可选的实参,该实参必须能转换为bool类型:如果实参是true,则函数不会抛出异常;如果实参是false,则函数可能抛出异常:

void recoup(int) noexcept (true);	//recoup不会抛出异常
void alloc(int) noexcept (false);	//alloc可能抛出异常

noexcept运算符

noexcept 说明符的实参常和 noexcept 运算符混合使用。
noexcept 运算符是一个一元运算符,它的返回值是一个bool类型的右值常量表达式,用于表示给定的表达式是否会抛出异常。noexcept 不会求其运算对象的值。

noexcept (recoup(i))//如果recoup不抛出异常则结果为true;否则结果为false

noexcept有两层含义:1.当跟在函数参数列表后面时它是异常说明符;2.当作为noexcept异常说明的bool实参出现时,它是一个运算符。

异常说明与指针、虚函数和拷贝控制

如果函数做了不抛出声明,那么指向它的函数指针也必须做不抛出声明。
如果一个虚函数做了不抛出声明,派生的函数也必须做不抛出声明。

异常类层次

在这里插入图片描述
类型exception仅仅定义了拷贝构造函数、拷贝赋值运算符、一个虚析构函数和一个名为what的虚成员。其中 what函数返回一个const char*,该指针指向一个以unll结尾的字符数组,并且确保不会抛出任何异常。

exceptionbad_castbad_alloc 定义了默认构造函数。
runtime_errorlogic_error 都没有默认构造函数,但有一个接受 C 风格字符串或标准库 string 类型实参的构造函数。
实际的应用程序常常会自定义 exception 的派生类来扩展继承体系。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/62455.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据结构入门 — 栈

本文属于数据结构专栏文章&#xff0c;适合数据结构入门者学习&#xff0c;涵盖数据结构基础的知识和内容体系&#xff0c;文章在介绍数据结构时会配合上动图演示&#xff0c;方便初学者在学习数据结构时理解和学习&#xff0c;了解数据结构系列专栏点击下方链接。 博客主页&am…

【C++】关于using namepace xxx 使用命名空间和冲突

官方定义 namespace是指 标识符的各种可见范围。命名空间用关键字namespace来定义。 命名空间是C的一种机制&#xff0c;用来把单个标识符下的大量有逻辑联系的程序实体组合到一起。此标识符作为此组群的名字。 基本使用 编译及执行命令&#xff1a; g test.cpp -o test ./…

汽车制造行业,配电柜如何实施监控?

工业领域的生产过程依赖于高效、稳定的电力供应&#xff0c;而配电柜作为电力分配和控制的关键组件&#xff0c;其监控显得尤为重要。 配电柜监控通过实时监测、数据收集和远程控制&#xff0c;为工业企业提供了一种有效管理电能的手段&#xff0c;从而确保生产的连续性、安全性…

Python程序化交易接口批量获取数据源码

小编举例下面是一个简单的示例代码&#xff0c;展示如何使用Python的程序化交易接口批量获取数据&#xff0c;例如开发文档参考&#xff1a;MetaTradeAPI (metatradeapi) - Gitee.com 签名 int Init(); 功能 API 初始化 参数 无 返回值 授权成功的交易账户数量 返回值 &…

实战系列(一)| Dubbo和Spring Cloud的区别,包含代码详解

目录 1. 概述2. 核心功能3. 代码示例4. 适用场景 Dubbo 和 Spring Cloud 都是微服务架构中的重要框架&#xff0c;但它们的定位和关注点不同。Dubbo 是阿里巴巴开源的一个高性能、轻量级的 RPC 框架&#xff0c;主要用于构建微服务之间的服务治理。而 Spring Cloud 是基于 Spri…

学术加油站|基于端到端性能的学习型基数估计器综合测评

编者按 本文系东北大学李俊虎所著&#xff0c;也是「 OceanBase 学术加油站」系列第 11 篇内容。 「李俊虎&#xff1a;东北大学计算机科学与工程学院在读硕士生&#xff0c;课题方向为数据库查询优化&#xff0c;致力于应用 AI 技术改进传统基数估计器&#xff0c;令数据库选…

Kubernetes技术--k8s核心技术持久化存储

有时候需要在集群中进行一些重要的数据进行持久化存储,然后需要的时候再进行挂载,那么下面我们一起来看看如何实现数据的持久化存储操作。 1.nfs网络存储 -1.找一台服务器做nfs的服务端,安装nfs。(这里我们直接在master上实现)。 这里应该找再单独的搭建一个node节点做持…

按钮控件之1---QPushButton 标准按钮/普通按钮控件

1、父类QAbstractButton 2、QPushButton按钮&#xff0c;是Qt常用的控件之一&#xff0c;提供普通的按钮功能。 通过信号槽机制接收触发信号并执行对应动作。3、创建QPushButton 它有三个构造函数&#xff1a; // 空对象 QPushButton(QWidget *parent nullptr); // 指定QPus…

基于Django+node.js+MySQL+杰卡德相似系数智能新闻推荐系统——机器学习算法应用(含Python全部工程源码)+数据集

目录 前言总体设计系统整体结构图系统流程图 运行环境Python 环境node.js前端环境MySQL数据库 模块实现1. 数据预处理2. 热度值计算3. 相似度计算1&#xff09;新闻分词处理2&#xff09;计算相似度 4. 新闻统计5. API接口开发6. 前端界面实现1&#xff09;运行逻辑2&#xff0…

文心一言 VS CHATGPT

由于近几天来&#xff0c;我的手机短信不断收到百度公司对于“文心一言”大模型的体验邀请&#xff08;真是不胜其烦&#xff09;&#xff01;&#xff01;所以我就抱着试试看的态度点开了文心一言的链接&#xff1a;文心一言 目前看来&#xff0c;有以下两点与chatgpt是有比较…

什么是浏览器缓存(browser caching)?如何使用HTTP头来控制缓存?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 浏览器缓存和HTTP头控制缓存⭐ HTTP头控制缓存1. Cache-Control2. Expires3. Last-Modified 和 If-Modified-Since4. ETag 和 If-None-Match ⭐ 缓存策略⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 记得点击…

新方案unity配表工具

工具下载&#xff1a;网盘链接 工具结构&#xff1a;针对每张表格生成一个表格类&#xff0c;其中默认包含一个list和字典类型参数记录表格数据&#xff0c;初始化项目时将list中的数据转为按id索引的dictionary&#xff0c;用于访问数据。额外包含一个同名Temp后缀的类&#…

5年前我们摸爬滚打进入测试行业,如今的你后悔吗?

记得在求职的时候&#xff0c;面试官经常问我&#xff1a;“为什么要选择软件测试工作?”而我也会经常说一堆自己有的没的优势去应付。 工作这么久了&#xff0c;也不再浮躁&#xff0c;静下心来回忆当初选择软件测试工作的历程&#xff0c;也是对自己职业生涯的一次回顾。 一…

部署java程序的服务器cpu过高如何排查和解决

1.top命令找到占用CPU高的Java进程PID 2.根据进程ID找到占用CPU高的线程 ps -mp pid -o THREAD,tid | sort -r ps -mp 124682 -o THREAD,tid | sort -r 3.将指定的线程ID输出为16进制格式 printf “%x\n” tid printf "%x\n" 6384 18f0 4.jstack pid |…

设计模式-原型模式详解

文章目录 前言理论基础1. 原型模式定义2. 原型模式角色3. 原型模式工作过程4. 原型模式的优缺点 实战应用1. 原型模式适用场景2. 原型模式实现步骤3. 原型模式与单例模式的区别 原型模式的变体1. 带有原型管理器的原型模式2. 懒汉式单例模式的原型模式实现3. 细粒度原型模式 总…

FPGA时序分析与约束(1)——组合电路时序

写在最前面&#xff1a; 关于时序分析和约束的学习似乎是学习FPGA的一道分水岭&#xff0c;似乎只有理解了时序约束才能算是真正入门了FPGA&#xff0c;对于FPGA从业者或者未来想要从事FPGA开发的工程师来说&#xff0c;时序约束可以说是一道躲不过去的坎&#xff0c;所以从这篇…

CSS魔术师Houdini,用浏览器引擎实现高级CSS效果

开门见山&#xff0c;直接上货 &#x1f50d; CSS Houdini是什么&#xff1f; “Houdini”一词引用自“Harry Houdini”&#xff0c;他是一位20世纪的著名魔术师&#xff0c;亦被称为史上最伟大的魔术师、逃脱术师及特级表演者。 我们都知道&#xff0c;浏览器在渲染网页显示样…

异或和大小比较类问题——抓住最高位:CF1863F

https://codeforces.com/contest/1863/problem/F 因为有等于&#xff0c;所以考虑异或和为0的合法区间&#xff0c;它可以随意切现在考虑切开后左边大于右边&#xff0c;可以发现左右边最高位可以互相抵消&#xff0c;似乎不太可做&#xff1f;此时可以换个考虑&#xff0c;考…

抖音企业号无需API开发连接AI图像生成,打造AI智能绘图助手

1. 抖音用户使用场景&#xff1a; 作为抖音企业号的运营人员&#xff0c;我们一直在寻找新的方式来增强我们与用户之间的互动。最近&#xff0c;我们发现了AI绘图技术可以根据用户需求和指令自动创建图片&#xff0c;无需人为干预&#xff0c;这为我们节省了人力和时间。因此&a…

node 如何下载任意版本

开门见山啦 第一步&#xff1a;打开node官网 Node.js 第二步&#xff1a;点击下载 进入下面的页面&#xff0c;然后往下滑&#xff0c;点击 All download options 查看以往所有的版本号&#xff1a; 这样就可以按自己的需求下载对应的node版本啦 或者 &#xff1a; 最简单…