当前位置: 首页 > news >正文

C++之异常

目录

一、C语言传统的处理错误的方式

二、C++的异常

1、概念

2、关键字

3、基本格式

三、异常的抛出和捕获

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

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

四、异常抛派生类,基类捕获

五、异常的重新抛出

六、异常安全

七、异常的优缺点


一、C语言传统的处理错误的方式

传统的错误处理机制:
1、 终止程序,如assert,缺陷:用户难以接受。如发生内存错误,除0错误时就会终止程序。
2、返回错误码,缺陷:需要程序员自己去查找对应的错误。如系统的很多库的接口函数都是通 过把错误码放到errno中,表示错误。
3、实际中C语言基本都是使用返回错误码的方式处理错误,部分情况下使用终止程序处理非常严重的错误。

二、C++的异常

1、概念

异常是一种处理错误的方式,当一个函数发现自己无法处理的错误时就可以抛出异常,让函数的直接或间接的调用者处理这个错误。

2、关键字

* throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。
* catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常,可以有多个catch进行捕获。
* try:try 块中的代码标识将被激活的特定异常,它后面通常跟着一个或多个 catch 块。
注:如果有一个块抛出一个异常,捕获异常的方法会使用 try 和 catch 关键字。try 块中放置可能抛出异常的代码,try 块中的代码被称为保护代码。

3、基本格式

try
{// 保护的标识代码
}
catch( ExceptionName e1 )
{// catch 块
}
catch( ExceptionName e2 )
{// catch 块
}
catch( ExceptionName eN )
{// catch 块
}
使用如下:
double Division(int a, int b)
{// 当b == 0时抛出异常if (b == 0)throw "Division by zero condition!";elsereturn ((double)a / (double)b);
}void Func()
{int len, time;cin >> len >> time;cout << Division(len, time) << endl;
}int main()
{try {Func();}catch (const char* errmsg) {cout << errmsg << endl;}catch(...) //可以捕获任意类型的异常{cout<<"unkown exception"<<endl;     }return 0;
}

一旦抛出了异常,那么程序会直接跳到 catch位置,即直接捕获异常。如果没有异常,则会正常执行,跳过 catch 操作。


三、异常的抛出和捕获

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

1、异常是通过抛出对象而引发的,该对象的类型决定了应该激活哪个catch的处理代码。
2、被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个。
3、抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,所以会生成一个拷贝对象,这个拷贝的临时对象会在被catch以后销毁。(这里的处理类似于函数的传值返回)。
4、catch(...)可以捕获任意类型的异常,问题是不知道异常错误是什么。
5、实际中抛出和捕获的匹配原则有个例外,并不都是类型完全匹配,可以抛出的派生类对象,使用基类捕获。

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

1、首先检查throw本身是否在try块内部,如果是再查找匹配的catch语句。如果有匹配的,则
调到catch的地方进行处理。
2、没有匹配的catch则退出当前函数栈,继续在调用函数的栈中进行查找匹配的catch。
3、如果到达main函数的栈,依旧没有匹配的,则终止程序。上述这个沿着调用链查找匹配的catch子句的过程称为栈展开。所以实际中我们最后都要加一个catch(...)捕获任意类型的异
常,否则当有异常没捕获,程序就会直接终止。
4、找到匹配的catch子句并处理以后,会继续沿着catch子句后面继续执行。

有三个函数:func1,func2,func3。在fun2中调用func1,在func3中调用func2,在main中调用func3,并在func1中抛出一个异常,在main函数中用catch语句捕获。

栈展开过程:首先检查throw本身是否在try块内部,如果是再查找匹配的catch语句。如果有匹配的,则处理。没有则退出当前函数栈,继续在调用函数的栈中进行查找,并不断重复上述过程,若达到main函数的栈,依旧没有匹配的,则终止程序。


四、异常抛派生类,基类捕获

实际使用中,人们一般都会自定义自己的异常体系进行规范的异常管理,因为在一个项目中,不同的负责组或者负责人可能会抛出不同的异常,如果没有规范,负责最外层捕获异常的人就需要捕获大家抛出的各种类型的异常。所以实际中都会定义一套继承的规范体系,这样大家抛出的都是继承的派生类对象,捕获一个基类就可以了。

下面我们就来举个例子:

首先一个规范的异常基类:

class Exception
{
public:Exception(const string& errmsg, int id):_errmsg(errmsg),_id(id){}virtual string what() const{return _errmsg;}
protected:string _errmsg;int _id;
};

两个不同负责组的异常派生类

class SqlException : public Exception
{
public:SqlException(const string& errmsg, int id, const string& sql):Exception(errmsg, id), _sql(sql){}virtual string what() const{string str = "SqlException:";str += _errmsg;str += "->";str += _sql;return str;}
private:const string _sql;
};class CacheException : public Exception
{
public:CacheException(const string& errmsg, int id):Exception(errmsg, id){}virtual string what() const{string str = "CacheException:";str += _errmsg;return str;}
};

 异常抛出程序:

void SQLMgr()
{srand(time(0));if (rand() % 7 == 0){throw SqlException("权限不足", 100, "select * from name = '张三'");}
}int main()
{while (1){this_thread::sleep_for(chrono::seconds(1));try{HttpServer();}catch (const Exception& e) // 这里捕获父类对象就可以{cout << e.what() << endl;}catch (...){cout << "Unkown Exception" << endl;}}return 0;
}

若SQL出现异常,那么就会抛出异常,且该异常会在main函数中的第一个catch位置被捕获。 因为从前面的多态中我们知道父类是可以接受子类的,这样就可以把不同的类型的异常统一起来,进行规范的管理。


五、异常的重新抛出

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

我们直接看下面的代码:

double Division(int a, int b)
{// 当b == 0时抛出异常if (b == 0){throw "Division by zero condition!";}return (double)a / (double)b;
}void Func()
{int* array = new int[10];int len, time;cin >> len >> time;cout << Division(len, time) << endl;cout << "delete []" << array << endl;delete[] array;
}int main()
{try{Func();}catch (const char* errmsg){cout << errmsg << endl;}return 0;
}

上面的代码在 Division中若出现了除0错误,那么就会抛出异常。而异常抛出后会直接跳到 main 函数的catch位置,那这时就会出现问题了,arry还没有释放,这样就发生了内存泄漏,这是无法容忍的,所以我们就需要在抛出异常后还能够释放arry。那就有了下面的代码:

double Division(int a, int b)
{// 当b == 0时抛出异常if (b == 0){throw "Division by zero condition!";}return (double)a / (double)b;
}void Func()
{int* array = new int[10];try {int len, time;cin >> len >> time;cout << Division(len, time) << endl;}catch (...){cout << "delete []" << array << endl;delete[] array;throw;}cout << "delete []" << array << endl;delete[] array;
}int main()
{try{Func();}catch (const char* errmsg){cout << errmsg << endl;}return 0;
}

Division中若出现了除0错误,那么就会抛出异常,该异常会被上一层函数Func捕获,然后释放掉arry。但是除0错误仍然存在,所以我们要重新抛出异常,使异常能够被main函数的catch捕获。这就是异常的重新抛出。


六、异常安全

1、构造函数完成对象的构造和初始化,最好不要在构造函数中抛出异常,否则可能导致对象不
完整或没有完全初始化。


2、析构函数主要完成资源的清理,最好不要在析构函数内抛出异常,否则可能导致资源泄漏(内
存泄漏、句柄未关闭等)。


3、C++中异常经常会导致资源泄漏的问题,比如在new和delete中抛出了异常,导致内存泄
漏,在lock和unlock之间抛出了异常导致死锁,C++经常使用RAII来解决以上问题。


七、异常的优缺点

1、优点

1、异常对象定义好了,相比错误码的方式可以清晰准确的展示出错误的各种信息,甚至可以包含堆栈调用的信息,这样可以帮助更好的定位程序的bug。


2、返回错误码的传统方式有个很大的问题就是,在函数调用链中,深层的函数返回了错误,那么我们得层层返回错误,最外层才能拿到错误。而在C++11中抛出的异常异常会直接跳到main函数中catch捕获的地方,main函数直接处理错误。

3、很多的第三方库都包含异常,比如boost、gtest、gmock等等常用的库,那么我们使用它们也需要使用异常。


4、 部分函数使用异常更好处理,比如构造函数没有返回值,不方便使用错误码方式处理。比如T& operator这样的函数,如果pos越界了只能使用异常或者终止程序处理,没办法通过返回值表示错误。

2、缺点

1、异常会导致程序的执行流乱跳,并且非常的混乱,并且是运行时出错抛异常就会乱跳。这会导致我们跟踪调试时以及分析程序时,比较困难。

2、C++标准库的异常体系定义得不好,导致大家各自定义各自的异常体系,非常的混乱。

3、C++没有垃圾回收机制,资源需要自己管理。有了异常非常容易导致内存泄漏、死锁等异常安全问题。这个需要使用RAII来处理资源的管理问题。

相关文章:

C++之异常

目录 一、C语言传统的处理错误的方式 二、C的异常 1、概念 2、关键字 3、基本格式 三、异常的抛出和捕获 1、异常的抛出和匹配原则 2、 在函数调用链中异常栈展开匹配原则 四、异常抛派生类&#xff0c;基类捕获 五、异常的重新抛出 六、异常安全 七、异常的优缺点…...

动态天气预报:Living Weather HD for Mac

Living Weather HD能够为Mac用户提供及时、准确、个性化的天气信息&#xff0c;并提供了丰富的定制选项&#xff0c;使用户能够更加方便地查看天气状况。 具有以下特点&#xff1a; 显示世界各地的准确天气预报和当地时间。自动探测出用户所在的首个地点&#xff0c;并通过搜…...

深度神经网络时与协方差矩阵

平时训练深度神经网络时&#xff0c;什么时候用到了协方差矩阵 在深度神经网络的平时训练过程中&#xff0c;一般情况下不直接使用协方差矩阵。然而&#xff0c;协方差矩阵的概念和相关性的考虑在某些情况下可以对网络的训练和优化起到一定的指导作用。 下面是一些与协方差矩…...

idea中java类属性(字段)链式赋值

很多人看到标题可能会想到 lombok 的 Builder&#xff0c;lombok 在国内用的挺多的&#xff0c;开源的组件中 mybatis-plus 中用到了这个&#xff0c;使用这个有一个问题就是通过对应 get 和 set 方法找不到对应的赋值方法&#xff0c;因为 lombok 使用了 apt 在编译期生成了相…...

vue通知(滚动)

1. li宽度不顾定 <template><div id"app"><div id"box" mouseover"clearLeft" mouseleave"setLeft"><ul :style"{ transform: translateX( left px) }" ref"cmdlist"><li v-for&qu…...

linux安装新版本git2、配置github-ssh。(centos、aws)

一、安装Git 1、yum默认版本git #1.安装git sudo yum install git -y #2.确认Git已经安装成功 git --version如果要安装较新版本&#xff0c;可以安装一个repo &#xff0c;但是我这第一次尝试失败了&#xff0c;执行完提示找不到git2u&#xff0c;ius repo也连不上。而且每次…...

毅速丨3D打印结合拓扑优化 让轻量化制造更容易

制造轻量化对于提高能源利用效率、提高产品性能和减少环境影响&#xff0c;推动制造业的绿色化、高质量发展具有重要的促进作用。 轻量化设计对许多领域都有着重要影响&#xff0c;尤其是那些需要降低能源消耗、提高运输效率或减少对环境影响的领域。如航空航天&#xff0c;轻量…...

6252: 【C1】【分支】比较大小(一)

目录 题目描述 输入 输出 样例输入 样例输出 提示 来源 C代码&#xff1a; 题目描述 输入两个整数&#xff0c;输出较大数&#xff08;两数相等输出任意一个&#xff09; 输入 两行 第一行一个整数&#xff1a;m 第二行一个整数&#xff1a;n ( -30000 < m , n…...

网工实验手册:RSTP如何配置?

1. 实验目的 熟悉RSTP的应用场景掌握RSTP的配置方法 想要华为数通配套实验拓扑和配置笔记的朋友们点赞关注&#xff0c;评论区留下邮箱发给你! 2. 实验拓扑 实验拓扑如图所示&#xff1a; 图&#xff1a;RSTP的配置 3. 实验步骤 &#xff08;1&#xff09; …...

uniapp开发h5引入第三方js(sdk)

manifest.json 应用配置 | uni-app官网 根据文档上描述需要自定义模板的场景为&#xff1a; 方法一&#xff1a; 起初以为是在原有的index.html基础上再新建一个html文件&#xff0c;在项目根目录建立一个template.h5.html&#xff08;仿照hello-uni-app项目&#xff09;&…...

Could not find artifact com.sleepycat;je:jar:7.3.7 in aliyunmaven

在编译inlong源码时报的错误&#xff0c;去本地库里发现只有lastupdate的文件&#xff0c;就又去maven库里看了一下Maven Repository: com.sleepycat je (mvnrepository.com)&#xff0c;发现没有这个版本&#xff0c;将版本进行修改错误解决...

rust学习—— 控制流if 表达式

控制流 根据条件是否为真来决定是否执行某些代码&#xff0c;或根据条件是否为真来重复运行一段代码&#xff0c;是大部分编程语言的基本组成部分。Rust 代码中最常见的用来控制执行流的结构是 if 表达式和循环。 if 表达式 if 表达式允许根据条件执行不同的代码分支。你提供…...

POSIX信号量

目录 信号量的原理 信号量函数 使用信号量实现线程互斥功能 基于环形队列的生产消费模型 生产者和消费者必须遵守的两个规则 信号量的原理 通过之前的学习&#xff0c;我们知道有的资源可能会被多个执行流同时申请访问&#xff0c;我们将这种资源叫做临界资源&#xff0c…...

stable diffusion和midjourney哪个好

midjourney和stable diffusion哪个好&#xff1f;midjourney和stable diffusion的区别&#xff1f;那么今天就从这2款软件入手&#xff0c;来探索一下他们的功能的各项区别吧&#xff0c;让你选择更适合你的一款ai软件。 截至目前&#xff0c;我们目睹了生成式人工智能工具的在…...

固件签名的安全解决方案 安当加密

在汽车行业中&#xff0c;加密机常用于对固件进行签名&#xff0c;以增加固件的安全性和完整性。以下是几个可能的使用场景&#xff1a; 固件验证&#xff1a;当汽车制造商或供应商需要对固件进行验证时&#xff0c;可以使用加密机来验证固件的来源和完整性。通过使用公钥和私…...

istio介绍(一)

1. 概念 1.1 虚拟服务 虚拟服务提供流量路由功能&#xff0c;它基于 Istio 和平台提供的基本的连通性和服务发现能力&#xff0c;让您配置如何在服务网格内将请求路由到服务 示例&#xff1a; apiVersion: networking.istio.io/v1alpha3 kind: VirtualService metadata:nam…...

基于鱼鹰优化的BP神经网络(分类应用) - 附代码

基于鱼鹰优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码 文章目录 基于鱼鹰优化的BP神经网络&#xff08;分类应用&#xff09; - 附代码1.鸢尾花iris数据介绍2.数据集整理3.鱼鹰优化BP神经网络3.1 BP神经网络参数设置3.2 鱼鹰算法应用 4.测试结果&#xff1a;5.M…...

【LeetCode】145. 二叉树的后序遍历 [ 左子树 右子树 根结点]

题目链接 文章目录 Python3方法一&#xff1a; 递归 ⟮ O ( n ) ⟯ \lgroup O(n) \rgroup ⟮O(n)⟯方法二&#xff1a; 迭代 ⟮ O ( n ) ⟯ \lgroup O(n) \rgroup ⟮O(n)⟯方法三&#xff1a; Morris ⟮ O ( n ) 、 O ( 1 ) ⟯ \lgroup O(n)、O(1) \rgroup ⟮O(n)、O(1)⟯写…...

Unity之ShaderGraph如何实现触电电流效果

前言 之前使用ASE做过一个电流效果的shader&#xff0c;今天我们通过ShaderGraph来实现一个电流效果。 效果如下&#xff1a; 关键节点 Simple Noise&#xff1a;根据输入UV生成简单噪声或Value噪声。生成的噪声的大小由输入Scale控制。 Power&#xff1a;返回输入A的结果…...

【微信小程序调试工具试用】

【微信小程序调试工具试用】 试用大佬开发的dll拿到某物小程序sign签名 &#xff08;过于简单 大佬勿喷&#xff09;本次工具分享到此结束 什么是爬虫逆向&#xff1f; 试用大佬开发的dll拿到某物小程序sign签名 &#xff08;过于简单 大佬勿喷&#xff09; 1 如图 下面小程序…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来&#xff0c;Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

Vue3 + Element Plus + TypeScript中el-transfer穿梭框组件使用详解及示例

使用详解 Element Plus 的 el-transfer 组件是一个强大的穿梭框组件&#xff0c;常用于在两个集合之间进行数据转移&#xff0c;如权限分配、数据选择等场景。下面我将详细介绍其用法并提供一个完整示例。 核心特性与用法 基本属性 v-model&#xff1a;绑定右侧列表的值&…...

ESP32 I2S音频总线学习笔记(四): INMP441采集音频并实时播放

简介 前面两期文章我们介绍了I2S的读取和写入&#xff0c;一个是通过INMP441麦克风模块采集音频&#xff0c;一个是通过PCM5102A模块播放音频&#xff0c;那如果我们将两者结合起来&#xff0c;将麦克风采集到的音频通过PCM5102A播放&#xff0c;是不是就可以做一个扩音器了呢…...

智能分布式爬虫的数据处理流水线优化:基于深度强化学习的数据质量控制

在数字化浪潮席卷全球的今天&#xff0c;数据已成为企业和研究机构的核心资产。智能分布式爬虫作为高效的数据采集工具&#xff0c;在大规模数据获取中发挥着关键作用。然而&#xff0c;传统的数据处理流水线在面对复杂多变的网络环境和海量异构数据时&#xff0c;常出现数据质…...

React---day11

14.4 react-redux第三方库 提供connect、thunk之类的函数 以获取一个banner数据为例子 store&#xff1a; 我们在使用异步的时候理应是要使用中间件的&#xff0c;但是configureStore 已经自动集成了 redux-thunk&#xff0c;注意action里面要返回函数 import { configureS…...

Java求职者面试指南:计算机基础与源码原理深度解析

Java求职者面试指南&#xff1a;计算机基础与源码原理深度解析 第一轮提问&#xff1a;基础概念问题 1. 请解释什么是进程和线程的区别&#xff1f; 面试官&#xff1a;进程是程序的一次执行过程&#xff0c;是系统进行资源分配和调度的基本单位&#xff1b;而线程是进程中的…...

【 java 虚拟机知识 第一篇 】

目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...

C语言中提供的第三方库之哈希表实现

一. 简介 前面一篇文章简单学习了C语言中第三方库&#xff08;uthash库&#xff09;提供对哈希表的操作&#xff0c;文章如下&#xff1a; C语言中提供的第三方库uthash常用接口-CSDN博客 本文简单学习一下第三方库 uthash库对哈希表的操作。 二. uthash库哈希表操作示例 u…...