C++(18):异常处理

news2024/11/22 20:48:01

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

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

抛出异常

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.coloradmin.cn/o/974694.html

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

相关文章

基于 Web HID API 的HID透传测试工具(纯前端)

前言 最近再搞HID透传 《STM32 USB使用记录&#xff1a;HID类设备&#xff08;后篇&#xff09;》 。 市面上的各种测试工具都或多或少存在问题&#xff0c;所以就自己写一个工具进行测试。目前来说纯前端方案编写这个工具应该是最方便的&#xff0c;这里放上相关代码。 项目…

通过idea实现springboot集成mybatys

概述 使用springboot 集成 mybatys后&#xff0c;通过http请求接口&#xff0c;使得通过http请求可以直接直接操作数据库&#xff1b; 完成后端功能框架&#xff1b;前端是准备上小程序&#xff0c;调用https的请求接口用。简单实现后端框架&#xff1b; 详细 springboot 集…

qt中子窗口最小化后再恢复显示窗口区域显示为全白色

问题&#xff1a; qt中子窗口最小化后再恢复显示窗口区域显示为全白色&#xff0c;如下图&#xff1a; 原因&#xff1a; 恢复显示后窗口为及时刷新。 解决办法&#xff1a; 重写showEvent函数&#xff0c;如下&#xff1a; void MyClass::showEvent(QShowEvent *event) {se…

OS | 第5章 插叙:进程API

OS | 第5章 插叙&#xff1a;进程API 文章目录 OS | 第5章 插叙&#xff1a;进程API5.1 fork()系统调用代码过程分析 5.2 wait()系统调用5.3 exec() 系统调用执行过程 为什么这样设计API&#xff1f;shell执行过程shell 重定向pipe()>>>>> 欢迎关注公众号【三戒…

YOLOv5:解读metrics.py

YOLOv5&#xff1a;解读metrics.py 前言前提条件相关介绍metrics.pyfitnesssmoothbox_iouConfusionMatrix ★ ★ \bigstar\bigstar ★★bbox_iou ★ ★ \bigstar\bigstar ★★compute_apap_per_class&#xff08;难度&#xff1a; ⋆ ⋆ ⋆ ⋆ ⋆ \star\star\star\star\star ⋆…

openpnp - 底部相机高级矫正后,底部相机看不清吸嘴的解决方法

文章目录 openpnp - 底部相机高级矫正后,底部相机看不清吸嘴的解决方法概述解决思路备注补充 - 新问题 - N1吸嘴到底部相机十字中心的位置差了很多END openpnp - 底部相机高级矫正后,底部相机看不清吸嘴的解决方法 概述 自从用openpnp后, 无论版本(dev/test), 都发现一个大概…

mac建议装双系统吗,详细分析苹果电脑双系统的利弊

mac建议装双系统吗&#xff0c;Mac电脑上安装双系统有哪些利弊呢&#xff0c;一起来看看吧&#xff01; 苹果Mac电脑安装双系统利&#xff1a; 1、用来办公更加方便&#xff1a;苹果系统功能也是很强大的&#xff0c;但是用来办公非常不方便&#xff0c;是由于一些常用的exe软…

Error: The project seems to require yarn but it‘s not installed.

把之前做过的vue项目拷贝到新电脑上&#xff0c;运行启动命令后发现报了如下错误&#xff1a; 我是这么解决的&#xff1a; 是因为项目中存在yarn.lock 文件&#xff0c;先把这个文件删除掉。 把这个文件删除后&#xff0c;执行如下命令&#xff1a; npm install -g yarn 下…

Docker Desktop 设置镜像环境变量

点击run 展开Optional settings container name &#xff1a;容器名称 Ports&#xff1a;根据你需要的端口进行输入&#xff0c;不输入则默认 后面这个 比如我这个 5432 Volumes&#xff1a;卷&#xff0c;也就是做持久化 需要docker 数据保存的地方 Environment variables…

TCP的滑动窗口协议有什么用?

分析&回答 滑动窗口协议&#xff1a; TCP协议的使用维持发送方/接收方缓冲区 缓冲区是 用来解决网络之间数据不可靠的问题&#xff0c;例如丢包&#xff0c;重复包&#xff0c;出错&#xff0c;乱序 在TCP协议中&#xff0c;发送方和接受方通过各自维护自己的缓冲区。通…

react16之前diff算法的理解和总结

此篇文章所讨论的是 React 16 以前的 Diff 算法。而 React 16 启用了全新的架构 Fiber&#xff0c;相应的 Diff 算法也有所改变&#xff0c;本片不详细讨论Fiber。 fiber架构是为了支持react进行可中断渲染&#xff0c;降低卡顿&#xff0c;提升流畅度。 react16之前的版本&…

什么是standard cell (标准单元) ?

参考文章&#xff1a; 聊一聊芯片后端的标准单元-standard cell - 知乎 (zhihu.com) standard cell中的7T和9T中的"T"指的是什么&#xff1f;或者是什么的缩写&#xff1f; - Layout讨论区 - EETOP 创芯网论坛 (原名&#xff1a;电子顶级开发网) - 数字后端基本概念介…

qemu-system-x86_64 命令创建虚拟机,报gtk initialization failed的

因为是ssh命令行启动&#xff0c;增加--nographic # /opt/debug/bin/qemu-system-aarch64 -machine virt-6.2 -qmp tcp:localhost:1238,server,nowait --nographic configure accelerator virt-6.2 start machine init start cpu init start add rom file: virtio-net-pci…

GIT实战篇,教你如何使用GIT可视化工具

系列文章目录 手把手教你安装Git&#xff0c;萌新迈向专业的必备一步 GIT命令只会抄却不理解&#xff1f;看完原理才能事半功倍&#xff01; 快速上手GIT命令&#xff0c;现学也能登堂入室 GIT实战篇&#xff0c;教你如何使用GIT可视化工具 系列文章目录一、GIT有哪些常用工具…

漆包线工厂云MES解决方案

漆包线行业老板痛点&#xff1a; 1.漆包线比较传统的行业&#xff0c;一般都是靠人工去管理&#xff0c;老板想及时知道工厂的生产&#xff0c;销售、出入库、库存情况&#xff1b; 2.型号多称重打印易错&#xff0c;没有系统前 &#xff1a;称重打印&#xff0c;出入库&#x…

解决github连接不上的问题

改 hosts 我们在浏览器输入 GitHub 的网址时&#xff0c;会向 DNS 服务器发送一个请求&#xff0c;获取到 GitHub 网站所在的服务器 IP 地址&#xff0c;从而进行访问。 就像你是一名快递员&#xff0c;在送快递前要先找中间人询问收件人的地址。而 DNS 就是这个告诉你目标地址…

java开发之个微机器人的二次开发

简要描述&#xff1a; 取消消息接收 请求URL&#xff1a; http://域名地址/cancelHttpCallbackUrl 请求方式&#xff1a; POST 请求头Headers&#xff1a; Content-Type&#xff1a;application/json 参数&#xff1a; 参数名类型说明codestring1000成功&#xff0c;1…

顶尖211“小清华”!强过985,不要错过它!

一、学校及专业介绍 西安电子科技大学&#xff08;Xidian University&#xff09;&#xff0c;简称“西电” &#xff0c;位于陕西省西安市&#xff0c;是中央部属高校&#xff0c;直属于教育部&#xff0c;为全国重点大学&#xff0c;位列国家“双一流”“211工程”&#xff…

宝塔面板开心版出问题升级到正版的解决方案,有效解决TypeError: ‘NoneType‘ object is not subscriptable

服务器之前图开心装了个宝塔面板的开心版&#xff0c;前几天突然出现问题&#xff0c;报错TypeError: ‘NoneType’ object is not subscriptable。没法正常打开软件商店&#xff0c;也没法修复和升级系统&#xff0c;很烦躁。因为里面很多业务还在跑&#xff0c;实在不想重装。…

呜呜呼呼无无话

姓名和手机号脱敏 function nameDesen(value) {if (!value) return return value.substring(0, 1) new Array(value.length).join(*) } const bklnameDesen(宝矿力) console.log(bkl) //宝**function telephoneDesen(value) {if (!value) return value value.toString()ret…