C++:异常

1. 异常的概念

C语言主要通过错误码的方式处理错误,错误码本质上就是对错误信息进行分类编号,拿到错误码以后还要去查询错误信息,比较麻烦。异常时抛出一个对象,这个对象可以涵盖更全面的信息。

 异常处理机制允许程序中独立开发的部分能在运行时就出现的问题进行通信并做出相应的处理,异常使得我们能够将问题的检测与解决问题的过程分开,程序的一部分负责检测问题的出现,然后解决问题的任务传递给程序的另一部分,检测环节无需知道问题的处理模块的所有细节。

 2. 异常的抛出和捕获

程序出现问题时,我们通过抛出(throw)一个对象来引发一个异常,该对象的类型以及当前的调用链决定了应该由哪个catch的处理代码来处理该异常。

被选中的处理代码是调用链中与该对象类型匹配且离抛出异常位置最近的那一个。根据抛出对象的类型和内容,程序的抛出异常部分告知异常处理部分到底发生了什么错误。当throw执行的时候throw后面的语句就不会执行了。程序就从throw的位置直接跳到与之匹配的catch模块(可能是在同一函数中,还可能是在调用链中的另一个函数中)

1.沿着调用链的函数可能提早退出。

2.一旦程序开始执行异常处理程序,沿着调用链创建的对象都将销毁。

抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个局部对象,所以会生成一个拷贝对象,这个拷贝对象会在catch子句后销毁。(类似函数的传值返回)

3. 栈展开

 抛出异常后,程序暂停当前函数的执行,开始寻找与之匹配的catch子句,首先检查throw本身是否在try块内部,如果在则查找匹配的catch语句,如果有匹配的,则跳到catch的地方进行处理。

如果当前函数中没有try/catch子句,或者有try/catch子句但是类型不匹配,则退出当前函数,继续 在外层调用函数链中查找,上述查找的catch过程被称为栈展开。

如果到达main函数,依旧没有找到匹配的catch⼦句,程序会调用标准库的 terminate 函数终止程序。

如果找到匹配的catch子句处理后,catch子句代码会继续执行。

#include<iostream>
#include<string>
using namespace std;double Divide(int a, int b)
{try{if (b == 0){string s("Divide 除数不能为0");throw s;}else{return (double)a / (double)b;}}catch(int errid){cout << errid << endl;}return 0;
}void Func1()
{int a, b;cin >> a >> b;try{cout<<Divide(a, b) << endl;}catch (const char* errmsg){cout << errmsg << endl;}int main()
{while (1){try{Func1();}catch (const string& errmsg){cout << errmsg << endl;}}return 0;
}

如上代码抛出异常后先查看Divide中的catch,不匹配,再沿调用链就会匹配到Func1里的catch还是不匹配,最后匹配到main里的catch语句。

4. 查找匹配的处理代码

如果抛出对象有多个catch类型匹配,就选择相距最近的那一个。

但是也有一些例外:

允许从非常量向常量进行类型转换,也就是权限缩小

允许数组转换成指向数组元素类型的指针,函数被转换成指向函数的指针

允许从派生类向基类类型的转换,实际中的继承体系基本都使用此方式设计

如果到main函数,异常仍旧没有被匹配就会终止程序,不是发生严重错误的情况下,我们是不期望程序终止的,所以一般main函数中最后都会使用catch(...),他可以捕获任意类型的异常,但是并不知道异常错误是什么

通过以下代码来验证

#include<thread>class Exception
{
public:Exception(const string& errmsg,int id):_errmsg(errmsg),_id(id){}virtual string what() const{return _errmsg;}int getid() const{return _id;}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;}
};
class HttpException :public Exception
{
public:HttpException(const string& errmsg,int id,const string& type):Exception(errmsg,id),_type(type){}virtual string what() const{string str = "HttpException:";str += _type;str += ":";str += _errmsg;return str;}
private:string _type;
};void SQLMgr()
{if (rand() % 7 == 0){throw SqlException("权限不足", 100, "select * from name = '张三'");}else{cout << "SQLMgr 调用成功" << endl;}
}void CacheMgr()
{if (rand() % 5 == 0){throw CacheException("权限不足", 100);}else if (rand() % 6 == 0){throw CacheException("数据不存在", 101);}else{cout << "CacheMgr 调用成功" << endl;}SQLMgr();
}
void HttpServer()
{if (rand() % 3 == 0){throw HttpException("请求资源不存在", 100, "get");}else if (rand() % 4 == 0){throw HttpException("权限不足", 100, "post");}else{cout << "HttpException调用成功" << endl;}CacheMgr();
}void HttpSever()
{if(rand() % 3 == 0){throw HttpException("请求资源不存在", 100, "get");}else if (rand() % 4 == 0){throw HttpException("权限不足", 101, "post");}else{cout << "HttpServer调用成功" << endl;}CacheMgr();
}
int main()
{srand(time(0));while (1){this_thread::sleep_for(chrono::seconds(1));try{HttpSever();}catch (const Exception& e)//这里捕获基类,基类对象和派生对象都可以被捕捉{cout << e.what() << endl;}catch (...)//三个点表示其余情况{cout << "Unkown Exception" << endl;}}return 0;
}

派生类和基类都可以被捕获基类的catch给捕获到。

5. 重新抛出异常

有时候我们不会因为一次尝试失败就直接放弃,要多尝试几次。

在catch到一个对象时,需要对错误进行分类,其中的某种异常错误需要进行特殊的处理,其他错误则重新抛出异常给外层调用链处理。捕获异常后需要重新抛出,直接throw就可以把捕获的对象直接给抛出去

 我们类利用上面实现的

void _SeedMsg(const string& s)
{if (rand() % 2 == 0){throw HttpException("网络不稳定,发送失败", 102, "put");}else if (rand() % 7 == 0){throw HttpException("你不是对方好友,发送失败", 103, "put");}else{cout << "发送成功" << endl;}
}
void SendMsg(const string& s)
{for (size_t i = 0; i < 4; i++){try{_SeedMsg(s);break;}catch (const Exception& e){//如果是102号错误,网=网络不稳定,将重新发送//捕获异常,else中不是102号错误,将异常重新抛出if (e.getid() == 102){if (i == 3)throw;cout << "开始第" << i + 1 << "次重试" << endl;}elsethrow;}}
}int main()
{srand(time(0));while (1){this_thread::sleep_for(chrono::seconds(1));string str;try{SendMsg(str);}catch (const Exception& e)//这里捕获基类,基类对象和派生对象都可以被捕捉{cout << e.what() << endl;}catch (...)//三个点表示其余情况{cout << "Unkown Exception" << endl;}}return 0;
}

上述代码中,第一次如果成功了就会走break跳出循环,反之被捕获,如果是102号错误并且没有尝试三次就重新尝试(抛出),如果不是102号错误就将异常重新抛出。main函数捕捉这些抛出的异常。

6. 异常安全问题

异常抛出后,后面的代码就不再执行了,前面申请了资源(内存、锁等),后面进行释放,但是中间可能会抛异常就会导致资源没有释放,这里由于异常就引发了内存泄漏,产生安全性的问题。中间我们需要捕获异常,释放资源后面再重新抛出,对于这个问题智能指针有更好的解决方式。

并且在析构函数中,如果要抛出异常也要谨慎处理,比如析构函数要释放10个资源,释放到第五个抛出异常,则也需要捕获处理,否则后面的5个资源就没释放,也会造成内存泄漏。

double Divide(int a, int b)
{if (b == 0){throw "Divide 除数不能为0";}return (double)a / (double)b;
}void Func1()
{int* array = new int[10];try{int a, b;cin >> a>> b;cout<<Divide(a, b)<<endl;}catch (...){cout << "delete []" << array << endl;delete[] array;throw;//将异常重新抛出}cout << "delete []" << array << endl;delete[] array;
}int main()
{try{Func1();}catch (const char* errmsg){cout << errmsg << endl;}catch (const exception& e){cout << e.what() << endl;}catch (...){cout << "Unkown Exception" << endl;}return 0;
}

7. 异常规范

对于用户与编译器来说,提前知道某个程序会不会抛出异常大有裨益,知道某个函数是否会抛出异常有助于简化和调用函数的代码。

C++98中函数参数列表的后面接throw(),表示函数不抛异常,函数参数列表后面接throw(type1,type2...)表示可能会抛出多种类型的异常,可能会抛出的类型用逗号进行分割。

//C++98
//表示这个函数只会抛出bad_alloc的异常
void Func1(std::size_t a,std::size_t b) throw(std::bad_alloc);//表示这个函数不会抛出异常
void Func2(std::size_t size, void* ptr) throw();

C++11对此进行了简化,函数参数列表后面加noexcept表示不会抛出异常,啥都不加表示可能会抛出异常

但是编译器并不会在编译时检查noexcept,也就是说如果一个函数用noexecpt修饰了,但是同时又包含了throw语句或者调用的函数可能会抛出异常,编译器函数会顺利编译通过(有些编译器可能会报警告)。但是如果一个声明了noexcept的函数抛出了异常,程序会调用terminate来终止程序

double Divide(int a, int b) noexcept
{if (b == 0){throw "Divide 除数不能为0";}return (double)a / (double)b;
}int main()
{try{int a, b;cin >> a >> b;cout << Divide(a, b) << endl;}catch(const char* errmsg){cout << errmsg << endl;}catch (...){cout << "Unkown Exception" << endl;}}

noexcept(expression)还可以作为一个运算符来检查一个表达式是否会抛出异常,可能会返回true,不会就返回false

	int i = 0;cout << noexcept(Divide(1, 2)) << endl;cout << noexcept(Divide(1, 0)) << endl;cout << noexcept(i++) << endl;

由于上面的Divide后面加了 noexcept 所以都不会抛异常,输出如下

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

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

相关文章

南京邮电大学算法设计-二叉树先序遍历算法动态演示

二叉树先序遍历算法动态演示 一、课题内容和要求 (1)实验目的&#xff1a; 本实验通过手动输入二叉树结点信息&#xff0c;构建相应的二叉树&#xff0c;并通过图形化界面动态演示先序遍历算法的过程。通过本次实验&#xff0c;我可以深入理解二叉树的数据结构、先序遍历算法…

【开源免费】基于Vue和SpringBoot的在线考试系统(附论文)

本文项目编号 T 624 &#xff0c;文末自助获取源码 \color{red}{T624&#xff0c;文末自助获取源码} T624&#xff0c;文末自助获取源码 网络的广泛应用给生活带来了十分的便利。所以把在线考试管理与现在网络相结合&#xff0c;利用java技术建设在线考试系统&#xff0c;实现…

高阶C语言之六:程序环境和预处理

本文介绍程序的环境&#xff0c;在Linux下对编译链接理解&#xff0c;较为简短&#xff0c;着重在于编译的步骤。 C的环境 在ANSI C&#xff08;标准C语言&#xff09;的任何一种实现中&#xff0c;存在两个不同的环境。 翻译环境&#xff1a;在这个环境中&#xff0c;源代码…

HarmonyOs鸿蒙开发实战(10)=>状态管理-对象数组的属性数据变更刷新UI,基于@Observed 和@ObjectLink装饰器

1.条件:基于HarmonyOs5.0.0版本. 2.功能要求&#xff1a;横向列表中每个景点的名称&#xff08;eg: 第二项 “灵隐寺” &#xff09;, 在通过天气接口拿到对应天气后&#xff0c;拼接到名称后面 > 变成&#xff08;“灵隐寺” 天气&#xff09;&#xff09; 3.老规矩先看…

快速上手Mybatis Plus并速通MybatisPlus所有知识点

目录 一、简介 1.1 概况 1.2 特性 二、快速入门 1.建表 2.引依赖 3.application.ymal文件 4.定义mapper继承BaseMapper 5.总结 三、Mybatis Plus的使用 1.常见注解 1.1 TableName 1.2 TableId 1.3 TableField 2.常见配置 3.BaseMapper的基础CRUD方法 4.Wrapper…

使用代理解决前端跨域问题详解

目录 前言1. 什么是跨域问题&#xff1f;1.1 同源策略的定义1.2 跨域问题的表现 2. 解决跨域问题的常见方法3. 在 Vite 中配置代理解决跨域问题3.1 环境准备3.2 配置代理3.2.1 配置基础路径3.2.2 配置 Vite 代理3.2.3 参数解释 3.3 验证代理功能 4. 深入分析与注意事项4.1 代理…

使用MaxKB搭建知识库问答系统并接入个人网站(halo)

首发地址&#xff08;欢迎大家访问&#xff09;&#xff1a;使用MaxKB搭建知识库问答系统并接入个人网站 前言 从OpenAI推出ChatGPT到现在&#xff0c;大模型已经渗透到各行各业&#xff0c;大模型也逐渐趋于平民化&#xff1b;从最开始对其理解、生成、强大的知识积累的惊叹&…

查看台式机主机支持的最大分辨率 | 显卡和显示器

通过检查显卡规格和型号&#xff0c;确认主机支持最大分辨率。 方法1&#xff1a;设置 在设置 -> 系统 -> 屏幕 及其内的高级显示设置 设置 -> 显示 方法2&#xff1a;cmd 在运行中输入“devmgmt.msc”&#xff0c;进入设备管理器界面&#xff0c;点击展开“显示适配…

若依权限控制

springbootvue2项目中的权限控制(若依项目) 步骤: 1.登录管理员账号,为普通用户增加权限按钮 绿色部分为权限控制字符 2.在后端对应的方法上增加权限控制(这里以删除操作为例):PreAuthorize(“ss.hasPermi(‘area:store:remove’)”) 3.在前端对应的按钮上增加权限控制:v-ha…

5G的SUCI、SUPI、5G-GUTI使用场景及关系

使用场景(来源于对23.501、23.502、33.501、23.003的理解) 1、UE初始注册时&#xff0c;根据HN Public Key把SUPI加密成SUCI&#xff0c;并发送初始注册请求 2、AMF转发SUCI给AUSF和UDM进行认证&#xff0c;并获取解密后的SUPI 3、AMF根据SUPI生成一个5G-GUTI&#xff0c;并保…

本地部署 excalidraw

本地部署 excalidraw 0. 引言1. 本地部署 excalidraw2. 访问 excalidraw 0. 引言 Excalidraw 编辑器是一款开源虚拟手绘白板&#xff0c;支持协作且端到端加密。 1. 本地部署 excalidraw git clone https://github.com/excalidraw/excalidraw.git; cd excalidrawvi docker-c…

企业数字化转型的战略指南:物联网与微服务架构的深度融合及应用解析

新时代下的企业数字化转型挑战与机遇 在当前全球经济和技术迅猛发展的背景下&#xff0c;企业数字化转型成为保持竞争力和创新的关键战略。物联网&#xff08;IoT&#xff09; 的兴起为企业提供了无数新的数据来源和运营模式&#xff0c;然而&#xff0c;如何有效整合这些数据…

vue3+vant实现弹幕循环播放~

1、效果图 <!-- 弹幕 --> <div style"height: 88px"><van-barragev-model"list"duration"5000":rows"rows":gap"gap":loop"loop"style"--move-distance: -345px" ><div class&quo…

字母异位词分组(java)

题目描述 给你一个字符串数组&#xff0c;请你将 字母异位词 组合在一起。可以按任意顺序返回结果列表。 字母异位词 是由重新排列源单词的所有字母得到的一个新单shilie 示例 1: 输入: strs ["eat", "tea", "tan", "ate", "n…

解决ValueError: Custom function inv is not defined in `extra_sympy_mappings`.

一、报错问题 ValueError: Custom function inv is not defined in extra_sympy_mappings. You can define it with, e.g., model.set_params(extra_sympy_mappings{inv: lambda x: 1/x}), where lambda x: 1/x is a valid SymPy function defining the operator. You can als…

深度学习基础练习:代码复现transformer重难点

2024/11/10-2024/11/18: 主要对transformer一些比较难理解的点做了一些整理&#xff0c;希望对读者有所帮助。 前置知识&#xff1a; 深度学习基础练习&#xff1a;从pytorch API出发复现LSTM与LSTMP-CSDN博客 【神经网络】学习笔记十四——Seq2Seq模型-CSDN博客 【官方双语】一…

GIS与Web开发结合的产物:WebGIS

WebGIS&#xff0c;其实是利用Web开发技术结合地理信息系统&#xff08;GIS&#xff09;的产物&#xff0c;它是一种通过Internet实现GIS交互操作和服务的最佳途径。 WebGIS通过图形化界面直观地呈现地理信息和特定数据&#xff0c;具有可扩展性和跨平台性。 它提供交互性&am…

PAT甲级 1071 Speech Patterns(25)

&#x1f7e0; 题目大意&#x1f7e2; 思路分析&#x1f535; 代码改进&#x1f7e4; 总结提炼 原题链接 &#x1f7e0; 题目大意 给定一串字符串&#xff0c;要求找出字符串中出现次数最多的单词。 输入 输入一行字符串&#xff0c;字符串长度不超过1048576&#xff0c;所有…

基于单片机的多功能环保宠物窝设计

本设计基于单片机设计的多功能环保宠物窝&#xff0c;利用温湿度传感器、压力传感模块、气味传感模块、红外测温传感器、通信模块、显示模块、清扫部件等&#xff0c;使其能够实现自动检测并调节温湿度、补充宠物食物、检测宠物体温健康并出现异常时进行报警、自动清扫消毒宠物…

Spring AOP面向切面的编程

一、场景设定和问题复现: 1.准备AOP项目:spring-aop-annotation pom.xml <dependencies><!--spring context依赖--><!--当你引入Spring Context依赖之后&#xff0c;表示将Spring的基础依赖引入了--><dependency><groupId>org.springframework…