【C++11】异常

前言

        上文我们学习到了C++11中类的新功能【C++11】类的新功能-CSDN博客
        本文我们来学习C++下一个新语法:异常

1.异常的概念

        异常的处理机制允许程序在运行时就出现的问题进行相应的处理。异常可以使得我们将问题的发现和问题的解决分开,程序的一部分负责检测,而另一部分负责处理,检测环节无需过多的细节。

        当发生错误时,C语言主要通过错误码的形式处理错误,错误码本质是将不同的错误信息进行编号,拿到错误码我们还需要进行查询才能得知具体是什么错误,比较麻烦。而异常则是抛出一个对象,这个对象可以涵盖对于这个错误的各种信息

2.异常的抛出和捕获

        当程序出现问题时,我们将会通过【throw】抛出一个对象来引发一个异常,该对象的类型以及当前的位置决定了由哪个catch来处理。

        被选中的catch是调用链中与给对象类型匹配且离抛出位置最近的那一个。根据抛出异常对象的类型和内容,程序的抛出异常部分会告知到底发生了什么错误。

        当throw执行时,thorw后面的代码将不再执行,就像break一样直接跳出。程序的执行从throw的位置直接跳到匹配的catch位置,catch可能在同一个函数中,有可能在调用链的其他函数中。这里有两个注意点:1.沿着调用链的函数可能会提前退出 ,2.一旦程序开始执行抛异常,沿着调用链创建的对象都将销毁

        抛出异常对象后,会产生一个异常对象的临时对象,因为抛出的异常可能的局部对象。这个临时对象会在catch语句后销毁

3.栈展开

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

        如果不匹配或者没有try内部,则退出当前函数,继续在外层调用函数链中查找,上述过程被称为栈展开

        如果一直查找到main函数,依然没有匹配的catch语句,那么程序就会调用标准库的terminate函数终止程序

        如果在后续的查找过程中匹配到了相应的catch语句,那么就执行catch语句以及下面的代码。

#include<iostream>
#include<string>
using namespace std;double Divide(int a, int b)
{try{//当b为0时抛出异常if (b == 0){string s("除数为0!");throw s;}else{return (double)a / b;}}catch(int errid) //抛异常时,类型不匹配。结束当前函数{cout << errid << endl;}
}void Func()
{int len, time;cin >> len >> time;try{cout << Divide(len, time) << endl;}catch (string errid)//类型匹配,直接跳到这里执行代码{cout << errid << endl;cout << __FUNCTION__ << ":" << __LINE__ << endl;}}int main()
{while(1){try{Func();}catch (string errid)//类型匹配,但只会跳到最近的catch进行匹配{cout << errid << endl;cout << __FUNCTION__ << ":" << __LINE__ << endl;}}
}
//如果抛异常时,所有catch都不匹配就会报错,程序停止。

4.查找匹配的处理代码

        一般情况下抛出的对象类型要和catch的接收类型完全一样,如果有多个catch与之匹配,会匹配其最近的catch

        但是也又特殊情况,允许从非常量向常量的类型转化,也是就权限缩小。允许数组转换成指向数组的指针,允许函数转换成函数指针;允许派生类转换为基类,这个在实践中非常常用,一般抛异常都是用这个设计的

        抛异常的匹配如果到了main函数仍然匹配不上,就会报错,程序停止。但是一般来说不是发生严重的错误,我们是不期望程序停止的,所以在main函数的最后我们一般会使用 catch(...),它可以捕捉任意类型的异常,但是并不能知道具体的异常是什么。

        通过继承基类实现不同类型是异常,为了统一捕捉在实践中我们一般都是在main函数中使用catch,try一定要和从catch一起使用

#include<iostream>
#include<thread>
using namespace std;
//每个异常模块都是继承Exception的派生类,每个异常模块都可以添加自己的数据
//最后捕获的时候,我们捕获基类就可以了//不同的异常类型都通过继承基类来实现
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:const 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("权限不足", 101, "post");}else{cout << "HttpServer调用成功" << endl;}CacheMgr();
}int main()
{srand(time(0));while(1){this_thread::sleep_for(chrono::seconds(1));try{HttpServer();}catch (const Exception& e) //派生类可以向基类进行转换,这里用基类进行捕获即可{cout << e.what() << endl;}catch (...)//为防止出现异常不匹配导致程序终止,使用 【...】 进行匹配{cout << "未知异常" << endl;}}
}

5.异常重新抛出

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

#include<iostream>
#include<string>
#include<thread>
using namespace std;//以下程序模拟展示聊天时发送消息的情况
//若不是网络信号导致无法发送,则抛出异常
//若是网络信号不好时抛出异常,尝试多次发送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 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:const string _type;
};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 SeedMsg(const string& s)
{for (int i = 0; i < 4; i++){try{_SeedMsg(s);}catch (Exception& e){if (e.getid() == 102){//重新尝试if (i == 3){//网络信号太差,不再过多尝试,重新抛出throw;}cout << "开始第" << i + 1 << "次尝试" << endl;}else{//异常重新抛出throw;}}}
}int main()
{srand(time(0));while(1){this_thread::sleep_for(chrono::seconds(1));try{SeedMsg("hello");}catch (Exception& e){cout << e.what() << endl;}}
}

6.异常安全问题

        当执行throw语句时,throw后面的语句将不再执行。当执行throw这个语句要跳出这个函数去匹配catch,但是这个函数之前申请了空间。这就会造成内存泄漏

        出现这种情况就需要我们重新捕获异常,释放资源后再重新抛出。这种处理方式是不好的,下篇文章我会讲到智能指针,使用智能指针来解决这个问题是更好的方法。

7.异常规范

        对于用户和编译器而言,预先知道函数会不会抛异常是很有意义的,这有助于简化调用函数的代码。

        C++98中函数后面加 throw( ),表示不会抛异常。函数后面加 throw(类型1,类型2,....),表示可能会抛出异常,如果会抛出多个类型的异常,使用逗号隔开。

        C++11中表示方式会更简便一些,函数后面加noexcept表示不会抛出异常而函数后面什么都不加则表示可能会抛出异常

        不过很搞笑的是,如果一个函数后面加了noexcept,但是函数包含throw语句或者包含可能会抛异常的函数,编译器会顺利的编译通过。但是当这个函数真正抛出异常时,程序会报错停止。

        noexcept(函数调用表达式) 还可以作为一个运算符,去检查函数是否可能会抛异常。如果不会抛异常返回true,如果可能会抛异常返回fasle

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

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

相关文章

Linux基础命令之目录管理——了解各种操作文件目录的命令,万字教学,超详细!!!(1)

文章目录 前言1、Linux文件系统1.1 核心特点1.2 重要目录结构1.3 文件类型1.4 文件和目录的命名规则1.5 文件与目录的定位方式 2、查看目录或文件的详细信息&#xff08;ls&#xff09;2.1 基本语法2.2 常用操作2.3 高级用法 3、切换目录&#xff08;cd&#xff09;3.1 常用操作…

在线caj转换word

CAJ格式是中国知网特有的一种文献格式&#xff0c;在学术研究等领域广泛使用&#xff0c;但有时我们需要将其转换为Word格式&#xff0c;方便编辑、引用文献。本文分享如何轻松将CAJ转换为word的转换工具&#xff0c;提高阅读和办公效率。 如何将CAJ转换WORD? 1、使用CAJ转换…

【现代深度学习技术】注意力机制05:多头注意力

【作者主页】Francek Chen 【专栏介绍】 ⌈ ⌈ ⌈PyTorch深度学习 ⌋ ⌋ ⌋ 深度学习 (DL, Deep Learning) 特指基于深层神经网络模型和方法的机器学习。它是在统计机器学习、人工神经网络等算法模型基础上&#xff0c;结合当代大数据和大算力的发展而发展出来的。深度学习最重…

SpringBoot 集成滑块验证码AJ-Captcha行为验证码 Redis分布式 接口限流 防爬虫

介绍 滑块验证码比传统的字符验证码更加直观和用户友好&#xff0c;能够很好防止爬虫获取数据。 AJ-Captcha行为验证码&#xff0c;包含滑动拼图、文字点选两种方式&#xff0c;UI支持弹出和嵌入两种方式。后端提供Java实现&#xff0c;前端提供了php、angular、html、vue、u…

边缘网关(边缘计算)

边缘网关是边缘计算架构中的关键组件&#xff0c;充当连接终端设备&#xff08;如传感器、IoT设备&#xff09;与云端或核心网络的桥梁。它在数据源头附近进行实时处理、分析和过滤&#xff0c;显著提升效率并降低延迟。 核心功能 协议转换 ○ 支持多种通信协议&#xff08;如…

OpenCV定位地板上的书

任务目标是将下面的图片中的书本找出来&#xff1a; 使用到的技术包括&#xff1a;转灰度图、提取颜色分量、二值化、形态学、轮廓提取等。 我们尝试先把图片转为灰度图&#xff0c;然后二值化&#xff0c;看看效果&#xff1a; 可以看到&#xff0c;二值化后&#xff0c;书的…

机器学习第一讲:机器学习本质:让机器通过数据自动寻找规律

机器学习第一讲&#xff1a;机器学习本质&#xff1a;让机器通过数据自动寻找规律 资料取自《零基础学机器学习》。 查看总目录&#xff1a;学习大纲 关于DeepSeek本地部署指南可以看下我之前写的文章&#xff1a;DeepSeek R1本地与线上满血版部署&#xff1a;超详细手把手指…

修改图像分辨率

在这个教程中&#xff0c;您将学习如何使用Python和深度学习技术来调整图像的分辨率。我们将从基础的图像处理技术开始&#xff0c;逐步深入到使用预训练的深度学习模型进行图像超分辨率处理。 一、常规修改方法 1. 安装Pillow库 首先&#xff0c;你需要确保你的Python环境中…

jsAPI

环境准备 1 安装nvm nvm 即 (node version manager)&#xff0c;好处是方便切换 node.js 版本 安装注意事项 要卸载掉现有的 nodejs提示选择 nvm 和 nodejs 目录时&#xff0c;一定要避免目录中出现空格选用【以管理员身份运行】cmd 程序来执行 nvm 命令首次运行前设置好国…

SCDN是什么?

SCDN是安全内容分发网络的简称&#xff0c;它在传统内容分发网络&#xff08;CDN&#xff09;的基础上&#xff0c;集成了安全防护能力&#xff0c;旨在同时提升内容传输速度和网络安全性。 SCDN的核心功能有&#xff1a; DDoS防御&#xff1a;识别并抵御大规模分布式拒绝服务…

Qt/C++开发监控GB28181系统/实时视频预览/视频点播/rtp解包解码显示

一、前言 通过gb28181做实时视频预览&#xff0c;也就是视频点播功能&#xff0c;是最重要的功能了&#xff0c;绝对是整个系统排第一重要的&#xff0c;这就是核心功能&#xff0c;什么设备注册、获取通道等都是为了实时预览做准备的&#xff0c;当然这个功能也是最难的&…

找银子 题解(c++)

题目 思路 首先&#xff0c;这道题乍一看&#xff0c;应该可以用搜索来做。 但是&#xff0c;搜索会不会超时间限制呢&#xff1f; 为了防止时间超限,我们可以换一种做法。 先创立两个二维数组&#xff0c;一个是输入的数组a&#xff0c;一个是数组b。 假设 i 行 j 列的数…

子集树算法文档

1.算法概述 子集树是一种 回溯算法&#xff0c;用于生成一个集合的所有子集。给定一个数组 arr&#xff0c;该算法递归地遍历所有可能的子集&#xff0c;并通过一个辅助数组 x 标记当前元素是否被选中。 2.算法特点 时间复杂度&#xff1a;O(2n)&#xff08;因为一个包含 n 个…

HTTP/1.1 host虚拟主机详解

一、核心需求&#xff1a;为什么需要虚拟主机&#xff1f; 在互联网上&#xff0c;我们常常希望在一台物理服务器&#xff08;它通常只有一个公网 IP 地址&#xff09;上运行多个独立的网站&#xff0c;每个网站都有自己独特的域名&#xff08;例如 www.a-site.com​, www.b-s…

amass:深入攻击面映射和资产发现工具!全参数详细教程!Kali Linux教程!

简介 OWASP Amass 项目使用开源信息收集和主动侦察技术执行攻击面网络映射和外部资产发现。 此软件包包含一个工具&#xff0c;可帮助信息安全专业人员使用开源信息收集和主动侦察技术执行攻击面网络映射并执行外部资产发现。 使用的信息收集技术 技术数据来源APIs&#xf…

Spring Web MVC响应

返回静态页面 第一步 创建html时&#xff0c;要注意创建的路径&#xff0c;要在static下面 第二步 把需要写的内容写到body内 第三步 直接访问路径就可以 返回数据ResponseBody RestController Controller ResponseBody Controller&#xff1a;返回视图 ResponseBody&…

‌鸿蒙PC正式发布:国产操作系统实现全场景生态突破

鸿蒙PC正式发布&#xff1a;国产操作系统实现全场景生态突破‌ 2025年5月8日&#xff0c;华为在深圳举办发布会&#xff0c;正式推出搭载鸿蒙操作系统的个人电脑&#xff08;PC&#xff09;&#xff0c;标志着国产操作系统在核心技术与生态布局上实现历史性跨越。此次发布的鸿蒙…

【计算机视觉】OpenCV实战项目:Text-Extraction-Table-Image:基于OpenCV与OCR的表格图像文本提取系统深度解析

Text-Extraction-Table-Image&#xff1a;基于OpenCV与OCR的表格图像文本提取系统深度解析 1. 项目概述2. 技术原理与算法设计2.1 图像预处理流水线2.2 表格结构检测算法2.3 OCR优化策略 3. 实战部署指南3.1 环境配置3.2 核心代码解析3.3 执行流程示例 4. 常见问题与解决方案4.…

Redis BigKey 问题是什么

BigKey 问题是什么 BigKey 的具体表现是 redis 中的 key 对应的 value 很大&#xff0c;占用的 redis 空间比较大&#xff0c;本质上是大 value 问题。 BigKey怎么找 redis-cli --bigkeysscanBig Key 产生的原因 1.redis数据结构使用不恰当 2.未及时清理垃圾数据 3.对业务预…

go-gin

前置 gin是go的一个web框架&#xff0c;我们简单介绍一下gin的使用 导入gin &#xff1a;"github.com/gin-gonic/gin" 我们使用import导入gin的包 简单示例&#xff1a; package mainimport ("github.com/gin-gonic/gin" )func main() {r : gin.Default(…