异常与使用

news2024/11/13 9:27:30

异常

  • 一、C语言传统的错误处理机制
  • 二、异常
    • 1、概念
    • 2、关键字
    • 3、示例
  • 三、异常的使用
    • 1、异常的抛出和匹配原则
    • 2、在函数调用链中异常栈展开匹配原则
    • 3、栈展开示意图
    • 4、示例代码
    • 5、运行结果
  • 四、异常的重新抛出
    • 1、作用
    • 2、示例代码
    • 3、运行结果
  • 五、异常安全
  • 六、异常规范
    • 1、概念
    • 2、示例代码
    • 3、运行结果
  • 七、异常体系
    • 1、C++标准库
      • (1)概念
      • (2)示意图
    • 2、自定义
      • (1)作用
      • (2)示意图
  • 八、异常的优缺点
    • 1、优点
    • 2、缺点

一、C语言传统的错误处理机制

  • 终止程序:使用assert函数或者程序错误(内存错误,除0错误等等),当其发生时就会终止程序,但这种方式用户难以接受。
  • 返回错误码:当程序出现错误时,返回错误编号(错误码)。例如,系统的很多库的接口函数都是通过把错误码放到errno中表示错误。但这种方式需要程序员根据错误码去查找对应的错误。
  • 在实际中,C语言基本都是使用返回错误码的方式处理错误,部分情况下使用终止程序处理非常严重的错误。

二、异常

1、概念

  • C++的异常处理机制通过try、catch和throw三个关键字,为开发者构建了一个结构清晰、易于理解的异常处理框架。
  • 异常是一种处理错误的方式,当一个函数发现自己无法处理的错误时就可以抛出异常,让函数的直接或间接的调用者处理这个错误。
  • 当程序执行到可能抛出异常的代码段时,可以使用try块将其包围起来。然后通过一个或多个catch块来捕获并处理可能发生的特定类型的异常。而throw关键字则用于在程序中显式地抛出异常,通知上层调用者当前代码遇到了无法继续执行的情况。
  • 异常机制不仅使得异常处理代码与正常业务逻辑代码分离,提高了代码的可读性和可维护性,还通过异常的传播机制,使得开发者能够在更高层次上统一处理异常,从而避免了错误处理的代码在程序中到处蔓延,导致代码结构混乱。

2、关键字

  • throw:抛出异常。当程序出现错误且有需要的异常抛出时,可以使用throw关键字抛出对应的异常。
  • catch:捕获异常。在想要处理异常的地方,通过(一个或多个)catch关键字捕获对应类型的异常。如果捕获到异常,则在对应的catch函数体内进行对应异常的处理程序或者操作。
  • try:try块(函数体)中的代码标识将被激活的特定异常,它(函数体)后面通常跟着一个或多个catch块捕获对应的异常并处理。
  • 如果有抛出异常,则需要使用try和catch关键字捕获异常。try 块中放置可能抛出异常的代码,其中的代码被称为保护代码。

3、示例

try
{
  // 保护的标识代码
}catch( ExceptionName e1 )
{
  // catch块
}catch( ExceptionName e2 )
{
  // catch块
}catch( ExceptionName eN )
{
  // catch块
}

三、异常的使用

1、异常的抛出和匹配原则

  • 异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个catch的处理代码。而被选中的catch块是调用链中与该抛出对象类型匹配且离抛出异常位置最近的那一个。
  • 因为抛出的异常对象可能是一个临时对象。所以,抛出异常对象后,会生成一个异常对象的拷贝。这个拷贝的对象会在被catch以后销毁,它的处理方式类似于函数的传值返回。
  • 当不知道异常的错误,即catch的类型不确定。可以使用参数包…(catch(…))的方式捕获任意类型的异常。
  • 抛出和捕获的匹配原则有个例外,即抛出和捕获的类型并不都是完全匹配的。可以抛出派生类对象,使用基类捕获。

2、在函数调用链中异常栈展开匹配原则

  • 栈展开为沿着调用链查找匹配的catch子句的过程。
  • 首先检查throw操作本身是否被try包含,即是否在try函数体内。如果是再查找匹配的catch语句,如果有匹配的,则跳到对应的catch块处处理对应的异常;如果没有匹配的,则退出当前函数栈,继续在调用函数的栈中查找匹配的catch。如果到达main函数的函数栈依旧没有匹配的catch,则终止程序。
  • 在实际中,最后都要加一个catch(…)捕获任意类型的异常,否则当抛出的异常没被捕获时,程序会直接终止。
  • 找到匹配的catch子句并处理异常后,会继续沿着catch子句后面的程序(代码)继续执行。即异常被处理后,程序还会继续正常执行,只不过异常出现到异常被捕获和处理之间的程序(代码)不会执行。

3、栈展开示意图

在这里插入图片描述

4、示例代码

class Test
{
public:
	Test()
	{
		cout << "Test()" << endl;
	}
	~Test()
	{
		cout << "~Test()" << endl;
	}
};

double Division(int len, int time)
{
	if (time == 0)
	{
		throw "除零错误";
	}
	return (double)len / (double)time;
}

void F1()
{
	throw 1;
}

void Func()
{
	Test t;
	try
	{
		int len, time;
		cin >> len >> time;
		cout << Division(len, time) << endl;

	}
	catch (const char* s)
	{
		cout << s << endl;
	}

	F1();
	cout << "snowdragon" << endl;
}

int main()
{
	try
	{
		Func();
	}
	catch (const char* s)
	{
		cout << s << endl;
	}
	catch (...)
	{
		cout << "未知异常" << endl;
	}
	return 0;
}

5、运行结果

在这里插入图片描述

四、异常的重新抛出

1、作用

  • 当单个的catch不能完全处理一个异常,在进行一些校正处理以后,希望再交给更外层的调用链函数来处理时,catch可以通过重新抛出,将异常传递给更上层的函数进行处理。

2、示例代码

double Division(int len, int time)
{
	if (time == 0)
	{
		throw "除零错误";
	}
	return (double)len / (double)time;
}

void Func2()
{
	int* array1 = new int[10];
	cout << "new[] array1" << endl;
	int* array2 = new int[20];
	cout << "new[] array2" << endl;

	try
	{
		int len, time;
		cin >> len >> time;
		cout << Division(len, time) << endl;
	}
	catch (...)
	{
		delete[] array1;
		cout << "delete[] array1, catch (...)" << endl;
		delete[] array2;
		cout << "delete[] array2, catch (...)" << endl;

		throw;
	}
	delete[] array1;
	cout << "delete[] array1, Func2()" << endl;
	delete[] array2;
	cout << "delete[] array2, Func2()" << endl;
}

int main()
{
	try
	{
		Func2();
	}
	catch (const char* errmsg)
	{
		cout << errmsg << endl;
	}
	return 0;
}

3、运行结果

在这里插入图片描述
在这里插入图片描述

五、异常安全

  • 构造函数的功能为完成对象的构造和初始化。所以,最好不要在构造函数中抛出异常,否则可能导致对象不完整或没有完全初始化。
  • 析构函数的功能为完成资源的清理。所以,最好不要在析构函数内抛出异常,否则可能导致资源泄漏,如内存泄漏、句柄未关闭等等。
  • C++中异常经常会导致资源泄漏的问题,比如在new和delete中抛出了异常,导致内存泄漏,在lock和unlock之间抛出了异常导致死锁。以上问题在C++中经常使用RAII来解决,RAII相关内容参见智能指针(RAII)。

六、异常规范

1、概念

  • 异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。 可以在函数的后面接throw(类型),列出这个函数可能抛掷的所有异常类型。列出的异常类型可以不抛出,没有列出的异常类型可以抛出。
  • 函数的后面接throw(),表示函数不抛异常。但函数体内还是可以抛异常且不会报错。
  • 若无异常接口声明,则此函数可以抛掷任何类型的异常。
  • C++11中的noexcept表示不会抛异常,如果抛出异常则会报错。

2、示例代码

void func1() throw(const char*, int*)
{
	throw 1.2;
}

void func2() throw()
{
	throw "snowdragon,func3()";
}

void func3() noexcept
{
	//throw "snowdragon,fun2()";
}

int main()
{
	try {
		func1();
	}
	catch (...)
	{
		cout << "func1(),异常" << endl;
	}

	try {
		func2();
	}
	catch (...)
	{
		cout << "func2(),异常" << endl;
	}

	try {
		func3();
	}
	catch (...)
	{
		cout << "func3(),异常" << endl;
	}
	return 0;
}

3、运行结果

在这里插入图片描述

七、异常体系

1、C++标准库

(1)概念

  • 标准异常的基类为exception。标准库的组件抛出的所有对象都来自此类,即基类exception和异常之间是以父子类层次结构组织起来的。因此,通过引用捕获基类exception,可以捕获所有标准异常。

(2)示意图

在这里插入图片描述

2、自定义

(1)作用

  • 虽然可以继承C++标准库中exception类实现自己的异常类,但是C++标准库设计的不够好用。
  • 在实际中可以定义一套继承的异常规范体系。当抛出的都是继承同一个基类的派生类对象时,捕获该基类就可以达到使用目的,即可以捕获对应的所有异常。

(2)示意图

在这里插入图片描述

八、异常的优缺点

1、优点

  • 异常对象定义好了,相比错误码的方式可以清晰准确的展示出错误的各种信息,甚至可以包含堆栈调用的信息,这样可以帮助使用者更好地定位程序的bug。
  • 返回错误码的传统方式在函数调用链中比较麻烦,当深层的函数返回错误码时,需要层层返回错误码,这样在最外层才能拿到对应的错误码。在这一过程中,如果不加以判断,则程序会继续运行,错误码也不会正常返回。而异常使用起来就相对来说比较简单,当它出现时,会直接跳转到对应的catch块中对对应的异常进行处理,使用者不需要对异常跳转途中的代码和函数调用链的处理做判断。
  • 很多的第三方库都包含异常,比如boost、gtest、gmock等等常用的库,使用它们也需要使用异常。
  • 部分函数使用异常更好处理,比如构造函数没有返回值,不方便使用错误码的方式处理;在重载[]函数(T& operator[](size_t pos))中,即在数组中查找元素并返回的函数中,查找的变量pos越界访问了只能使用异常或者终止程序处理,没办法通过返回值表示错误。
  • 异常总体而言利大于弊,OO(面向对象)的语言基本都是用异常处理错误的。

2、缺点

  • 运行时出错抛异常会导致程序的执行流乱跳且非常的混乱,导致在跟踪调试以及分析程序时比较困难。
  • 异常会有一些性能的开销,但在现代硬件速度很快的情况下,这个影响基本忽略不计。
  • C++没有垃圾回收机制,资源需要自己管理。而异常非常容易导致内存泄漏、死锁等异常安全问题。这个需要使用RAII来处理资源的管理问题,即学习成本较高。
  • C++标准库的异常体系定义得不好,导致大家各自定义各自的异常体系,非常的混乱。
  • 异常尽量规范使用,如果随意抛异常,外层捕获异常和处理的程序就会非常复杂和难。所以,异常规范有两点,其一为抛出异常的类型都继承同一个基类;其二为函数是否抛异常、抛什么类型的异常,都使用 func() throw(异常类型); 的方式规范化。

本文到这里就结束了,如有错误或者不清楚的地方欢迎评论或者私信
创作不易,如果觉得博主写得不错,请点赞、收藏加关注支持一下💕💕💕

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

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

相关文章

CSS-浮动【看这一篇就够了】

目录 浮动布局 浮动是如何工作的 浮动的本质和要点 如何产生浮动 元素浮动的特性 1.元素添加浮动后&#xff0c;脱离文档流 2.如果父元素的宽度不够 3.浮动的顺序贴靠特性 4.元素浮动后&#xff0c;具有行内块级元素特性 5.浮动的元素会造成父元素高度塌陷 6.浮动对…

“无法连接打印机0X0000011B”原因分析及多种解决方法

在日常办公和生活中&#xff0c;打印机是不可或缺的重要设备。然而&#xff0c;有时在连接打印机的过程中&#xff0c;我们可能会遇到错误代码0x0000011b的提示。有更新补丁导致的、有访问共享打印机服务异常、有访问共享打印机驱动异常等问题导致的&#xff0c;针对访问共享打…

MySQL场景测试题

第一题 软件环境描述&#xff1a; Mysql V5.7.30 Innodb RR隔离级别 表结构以及数据描述&#xff1a; &#xff08;1&#xff09;t_user用户表&#xff0c;表格如下&#xff1a; CREATE TABLE t_user ( id int(10) NOT NULL, name varchar(100) DEFAULT NULL, PRIMARY KEY (id)…

240831-Gradio之RAG文档对话工具Kotaemon的安装与配置

A. 用户界面 该项目既可以作为功能性 RAG UI&#xff0c;既可以用于对文档进行 QA 的最终用户&#xff0c;也可以用作想要构建自己的 RAG 管道的开发人员。对于最终用户&#xff1a; - 一个干净且简约的用户界面&#xff0c;用于基于RAG的QA。 - 支持 LLM API 提供程序&#xf…

gethub-rrsf

一.FastCGI协议 1.来到127.0.0.1下发现404报错 2.这一关我们要借助一个叫Gopherus的工具&#xff0c;我这里是在kali虚拟机里面克隆的 git clone https://github.com/tarunkant/Gopherus.git 3.运行命令 由于一句话木马无法写入&#xff0c;所以我们使用base64编码&#xf…

将Google Chrome或Microsoft Edge浏览器的地址栏隐藏的方法

将Google Chrome或Microsoft Edge浏览器的地址栏隐藏的方法 目标效果示范 我们以百度首页为例&#xff0c;普通模式启动的页面通常会显示地址栏&#xff0c;如下图所示&#xff1a; 而本文要实现的效果是隐去地址栏和书签栏&#xff08;如果有的话&#xff09;&#xff0c;无…

重生奇迹MU 敏捷流梦幻骑士 真正的平民PK王

“梦幻骑士”这个职业已经存在于重生奇迹MU中很长时间了&#xff0c;虽然现在已经不算是新职业了&#xff0c;但玩家们对于梦幻骑士的研究和开发一直没有停止过。它作为一个特殊的职业&#xff0c;与传统职业截然不同&#xff0c;拥有着许多独特的玩法。其中&#xff0c;有一种…

JVM2-JVM组成、字节码文件、类的生命周期、类加载器

Java虚拟机的组成 Java虚拟机主要分为以下几个组成部分&#xff1a; 类加载子系统&#xff1a;核心组件类加载器&#xff0c;负责将字节码文件中的内容加载到内存中运行时数据区&#xff1a;JVM管理的内存&#xff0c;创建出来的对象、类的信息等内容都会放在这块区域中执行引…

有宠物用哪个牌子的宠物空气净化器,希喂、IAM哪个更值得推荐

由于很喜欢猫咪和狗狗&#xff0c;每天都只想和它们待在一起&#xff0c;一点都不想上班&#xff0c;经过一番深思熟虑后&#xff0c;决定裸辞去开了一家宠物店。还真别说&#xff0c;开了宠物店之后&#xff0c;整个人都舒爽了&#xff0c;还可以摸到很多不同品种的小猫小狗&a…

学习笔记之JS(0830)

1、介绍 1.1 JavaScript &#xff08;是什么&#xff1f;&#xff09; javascript是一种运行在客户端&#xff08;浏览器&#xff09;的编程语言&#xff0c;实现人机交互效果。作用&#xff08;做什么&#xff1f;&#xff09; 网页特效&#xff08;监听用户的一些行为让万叶…

Java 集合框架与泛型实战指南

Collection&#xff1a; Collection 不唯一&#xff0c;无序 List 不唯一&#xff0c;有序 Set 唯一&#xff0c;无序 ArrayList&#xff1a;内部使用长度可变的数组&#xff0c;遍历查询效率高 LinkedList&#xff1a;采用双向链表实现&#xff0c;增删改效率比较高 ArrayL…

【智能排班系统】Hibernate Validator 参数校验

&#x1f3af;导读&#xff1a;本文档介绍了参数校验的重要性及其在软件开发中的作用&#xff0c;强调了数据完整性、安全性、用户体验、系统稳定性及开发效率等方面的关键价值。文档详细阐述了Hibernate Validator这一流行的Java验证框架的使用方法&#xff0c;展示了如何利用…

适马相机cf卡剪切的数据还能恢复吗?可尝试这几种方法

“本想把适马相机CF卡里的珍贵数据剪切到电脑上&#xff0c;‌以备不时之需&#xff0c;‌但是不知道怎么回事&#xff0c;剪切后数据既不在电脑上&#xff0c;‌CF卡里也没了&#xff0c;这可真是让我心急如焚&#xff01;‌求大神指点迷津&#xff0c;‌帮我找回那些重要的文…

Vue 选项式api和组合式api 路由嵌套

选项式api和组合式api是两种不同的语法习惯&#xff0c;<template>标签内还是该怎么写就怎么写&#xff0c;不一样的只是<script>里面的语法改变了。 目录 选项式api&#xff1a; 组合式api&#xff1a; 1)省略各种关键字&#xff1a; 省略前&#xff1a; 省略后…

【Qt】菜单栏

目录 菜单栏 例子&#xff1a;创建菜单栏、菜单、菜单项 例子&#xff1a;给菜单设置快捷键 例子&#xff1a;给菜单项设置快捷键 例子&#xff1a;添加子菜单 例子&#xff1a;添加分隔线 例子&#xff1a;添加图标 菜单栏 Qt中的菜单栏是通过QMenuBar这个类实现的&…

LeetCode --- 412周赛

题目列表 3264. K 次乘运算后的最终数组 I 3266. K 次乘运算后的最终数组 II 3265. 统计近似相等数对 I 3267. 统计近似相等数对 II 一、K次乘预算后的最终数组 I & II I 数据范围比较小&#xff0c;可以暴力模拟&#xff0c;代码如下 class Solution { public:vecto…

Day52 | dijkstra(堆优化版)Bellman_ford 算法

dijkstra&#xff08;堆优化版&#xff09; 题目 47. 参加科学大会 47. 参加科学大会&#xff08;第六期模拟笔试&#xff09; 题目描述 小明是一位科学家&#xff0c;他需要参加一场重要的国际科学大会&#xff0c;以展示自己的最新研究成果。 小明的起点是第一个车站&a…

vscode 未定义标识符 “uint16_t“C/C++(20) 但是可以顺利编译

这是没有指定编译器的原因 解决方法&#xff1a; 打开 或c_cpp_properties.json&#xff0c;添加编译器

★ 算法OJ题 ★ 力扣611 - 有效三角形的个数

Ciallo&#xff5e;(∠・ω< )⌒☆ ~ 今天&#xff0c;椎名日和将和大家一起做一道双指针算法题--有效三角形的个数~ 目录 一 题目 二 算法解析 三 编写算法 一 题目 二 算法解析 给三个数&#xff0c;判断是否能构成三角形的条件&#xff1a;两个较小的数相加大于…

机器学习数学公式推导之高斯分布

文章目录 1、介绍引入1.1 频率派的观点1.2 贝叶斯派的观点1.3 小结 2、数学基础2.1 二阶中心矩2.2 样本方差2.3 高斯分布2.3.1 一维情况 MLE2.3.2 多维情况 本文参考 B站UP: shuhuai008 跳转 &#x1f339;&#x1f339; 1、介绍引入 在统计学和概率论中&#xff0c; P ( x ∣ …