C++_异常

       

目录

1、异常的关键字

2、异常的写法

3、异常的使用规则

3.1 规则1

3.2 规则2

3.3 规则3 

3.4 规则4

3.5 规则5 

4、异常的重新抛出 

5、异常的规范 

5.1 C++98的异常规范

5.2 C++11的异常规范

6、C++标准库的异常体系

7、异常的优缺点

结语 


前言:

        C++的异常是一种处理程序bug的方式,如今面向对象的语言基本都是用异常处理错误的,因为他可以帮助程序员快速定位错误的根源。当一个函数或者是一段代码发现了自己处理不了的错误就可以将该错误抛出,又称异常抛出,由外部的调用者或间接调用者捕获该错误,又称异常捕获

1、异常的关键字

         若要使用异常则需要用到三个关键字:try、throw、catch。他们的具体用法如下:

        try:try有属于自己的作用域,在他的作用域中存放可能会抛出异常的代码或者函数。

        throw:throw有着“抛出”的动作,throw后面跟一段内容,当程序出现错误时,throw会抛出其后面的内容(即抛出异常),作为错误的描述,通常throw后面跟一段字符串作为错误描述。

        catch:catch是捕获的意思,并且他只能捕获try里面代码的异常。当throw抛出了异常,那么就需要catch捕获该异常,不过要先捕获该异常,catch的捕获类型必须与throw抛出内容的类型一致才可以捕获。(具体看下文举例)

2、异常的写法

        异常的代码格式如下:

try
{// 可能会抛异常的代码
}catch( type e1 )//抛出的异常用形参e1捕获
{// 通常对e1的内容进行打印
}catch( type e2 )//可以有不同的捕获类型
{// 如上
}catch( type en )
{// 如上
}

        举例说明:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;int main()
{try{int a;cin >> a;if(a < 0)//如果输入的a小于0说明发生错误throw "a为负数发生错误";//抛异常:a为负数发生错误(类型是char*)}catch (const char* str)//因为抛出的因此类型是字符串,因此可以用char*接收{cout << str << endl;//打印异常的错误}return 0;
}

         运行结果:

3、异常的使用规则

3.1 规则1

        抛出异常和捕获异常的类型必须匹配,否则是捕获不了该异常的,若不匹配先会去找其他的catch能否类型匹配,若没找到则说明抛出的异常没有对应的捕获类型,则会报错。

        示例如下图:

3.2 规则2

        抛出的异常会被离该异常最近位置的catch捕获(前提是该catch与异常类型匹配),示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;void _func()
{int a = -2;if (a < 0)//如果输入的a小于0说明发生错误throw "a为负数发生错误";//抛异常:a为负数发生错误
}void func()
{try{_func();//复用}catch (const char* str)//func函数的捕获{cout << "void func():" << str << endl;//打印异常的错误}
}int main()
{try{func();}catch (const char* str)//main函数的捕获{cout << "main()" << str << endl;//打印异常的错误}return 0;
}

         运行结果:

        从结果可以看到,该异常被离他最近的func函数所捕获,假如此时的func函数的捕获类型不能与异常类型相匹配,才会让main函数中的catch去捕获。 

3.3 规则3 

        若catch捕获到了该try中的异常,则会从捕获该异常的catch之后开始执行剩下的代码(跳过其他的catch) ,示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;void _func()
{int a = -2;if (a < 0)//如果输入的a小于0说明发生错误throw "a为负数发生错误";//抛异常:a为负数发生错误
}void func()
{_func();//复用
}int main()
{try{func();}catch (const char* str){cout << str << endl;//打印异常的错误}catch (const int* str)//若异常已经被捕获,则会跳过该catch{cout << str << endl;}cout << "继续执行catch的下面代码" << endl;return 0;
}

        运行结果:

3.4 规则4

        当一个函数栈帧中抛出了异常,则编译器先会在该栈帧中查看是否有合适的catch捕获,若没有则结束该栈帧,回到调用该函数的上一层栈帧中查看是否有合适的catch,若没有也结束当前栈帧,直到回到main函数中若还没有合适的catch则直接报错(参考规则1),该过程又叫栈回收

        具体示意图如下:

         所以,从上面可以发现,如果没有合适的catch,则程序会被直接终止且报错,为了避免这种极端情况,一般会用catch(...)来接收任意类型的异常。

         万能捕获catch(...)示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;void _func()
{int a = -2;if (a < 0)//如果输入的a小于0说明发生错误throw "a为负数发生错误";//抛异常:a为负数发生错误
}void func()
{_func();//复用
}int main()
{try{func();}catch (const int* str){cout << str << endl;//打印异常的错误}catch (...)//万能捕获{cout << "其他类型错误" << endl;//打印异常的错误}return 0;
}

        运行结果:

         从结果可以看到,只要有了万能类型捕获,则无论是什么样类型的异常都不会因为没能捕获而导致程序直接被终止。

3.5 规则5 

         抛出的异常也可以是一个对象,这时候抛出的并不是对象本身,因为catch在别的作用域,所以抛出的是该对象的一个拷贝对象,并且该拷贝对象的生命周期会在catch之后销毁,所以catch的形参可以用引用接收,该形参是拷贝对象的别名而不是抛出的局部对象的别名。示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;void _func()
{int a = -2;if (a < 0)//如果输入的a小于0说明发生错误throw string("a为负数发生错误");//抛出的是string类型
}void func()
{_func();//复用
}int main()
{try{func();}//捕获的类型是string,并且可以用引用,因为返回的是拷贝对象catch (const string& str){cout << str << endl;//打印异常的错误}catch (...)//万能捕获{cout << "其他类型错误" << endl;//打印异常的错误}return 0;
}

        运行结果:

4、异常的重新抛出 

        异常的重新抛出是为了解决空间资源和捕获异常同时出现的情况,上文提到捕获异常时,如果在别的函数栈帧中才捕获该异常,则编译器会结束之前的栈帧,那么如果之前的栈帧里涉及到空间资源的问题,则会引发一个问题:其他栈帧里的空间还没来得及释放就因为要捕获异常而结束了该栈帧,会导致内存泄漏的问题。

        示例代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;void _func()
{int a = -2;if (a < 0)//如果输入的a小于0说明发生错误throw string("a为负数发生错误");//抛出的是string类型
}void func()
{int* arr = new int;//涉及到空间资源的管理try{_func();}catch (...)//函数func中必须捕获该异常才能释放申请的空间,否则直接跳到main函数了{cout << "delete arr" << endl;delete arr;throw;//重新抛出的写法}cout << "delete arr" << endl;delete arr;
}int main()
{try{func();}//捕获的类型是string,并且可以用引用,因为返回的是拷贝对象catch (const string& str){cout << str << endl;//打印异常的错误}catch (...)//万能捕获{cout << "其他类型错误" << endl;//打印异常的错误}return 0;
}

        运行结果:

5、异常的规范 

        异常的规范可以让函数的使用者清晰的看到该函数会抛出什么类型的异常或会不会抛出异常。

5.1 C++98的异常规范

        throw()是C++98的异常规范关键字,他通常写在函数接口的后面,具体用法如下:

// 这里表示这个函数会抛出int,char,char*中的某种类型的异常
void func() throw(int,char,char*);// 这里表示这个函数不会抛出异常
void func() throw();//在异常规范中,没有声明throw()则表示该函数可能抛出任意类型的异常
void func()

5.2 C++11的异常规范

        C++11对应异常规范新增了一个关键字:noexcept,该关键字同样写在函数的接口后面,表示该函数不会抛任何的异常,用法如下:

//表示func不会抛异常
func() noexcept;

        并且noexcept严格执行规范,若一个函数加了noexcept结果该函数会抛出异常,那么运行后就直接报错,并且复用该函数的函数也会判断为会抛出异常的函数。

        测试代码如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
using namespace std;void _func()
{int a = -2;if (a < 0)//如果输入的a小于0说明发生错误throw string("a为负数发生错误");//抛出的是string类型
}void func()noexcept//给func函数加上noexcept,事实上该函数会抛异常,运行后报错
{int* arr = new int;//涉及到空间资源的管理try{_func();}catch (...)//函数func中必须捕获该异常才能释放申请的空间,否则直接跳到main函数了{cout << "delete arr" << endl;delete arr;throw;//重新抛出的写法}cout << "delete arr" << endl;delete arr;
}int main()
{try{func();}//捕获的类型是string,并且可以用引用,因为返回的是拷贝对象catch (const string& str){cout << str << endl;//打印异常的错误}catch (...)//万能捕获{cout << "其他类型错误" << endl;//打印异常的错误}return 0;
}

        运行结果:

6、C++标准库的异常体系

        C++标准库中也提供了一部分异常信息可供用户捕获,以便让程序员使用时可以更好的定位错误根源。这些异常类型是以继承和多态的结构存在的,他们都有一个共同的父类exception,所以程序员只需要用该父类作为捕获的类型,然后根据多态的条件(父类指针或引用会调用子类的虚函数)就可以打印出来正确的错误信息了。

        关系图:

        具体异常信息如下图:

         标准库里的异常信息使用方式如下:

#define _CRT_SECURE_NO_WARNINGS 1#include<iostream>
#include<vector>
using namespace std;int main()
{try {vector<int> v1;// 这里如果系统内存不够也会抛异常v1.reserve(1000000000);}catch (const exception& e) // 这里捕获父类对象就可以{cout << e.what() << endl;}return 0;
}

        运行结果:

7、异常的优缺点

         异常的优点:

        1、异常可以展现出清晰的错误信息,以便帮助程序员快速的定位错误位置。

        2、异常可以直接抛出给最外层,这样一来可以在统一的地方进行处理异常,即使复用的函数栈帧很深时也无需每层栈帧都检查一遍是否有异常。

        3、一些常用的库也存在异常的概念,如boost、gtest、gmock。

        4、有些特殊的函数不能有返回值,比如构造函数,因此不能用错误码一层层返回的方式来定位构造函数的错误,所以构造函数出了bug只能用异常发现问题。

        异常的缺点:

        1、 捕获异常时若涉及空间资源管理则稍不谨慎就会发生内存泄漏。

        2、因为抛出的异常会直接跳到main函数中,所以有些场景下调试起来不方便。

        3、异常规范并不是强制行为,因此可能会出现有些代码没加noexcept,实际上却没有抛异常的可能。

        总结:总而言之异常还是利大于弊的。

结语 

        以上就是关于C++异常的讲解,异常在程序中发挥着至关重要的作用,有了异常就能快速定位bug,帮助程序员节省了改bug的时间,最后希望本文可以给你带来更多的收获,如果本文对你起到了帮助,希望可以动动小指头帮忙点赞👍+关注😎+收藏👌!如果有遗漏或者有误的地方欢迎大家在评论区补充,谢谢大家!!  

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

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

相关文章

学习数据节构和算法的第15天

单链表的实现 链表的基本结构 #pragma once #include<stdio.h> typedf int SLTDataType; typedy struct SListNode {SLTDataType data;struct SListNode*next; }SLTNode;void Slisprint(SLTNode*phead);打印链表 #include<stdio.h> void SListPrint(SLTNode*phe…

详解DSLS达索许可管理器的安装与配置

DSLS的安装与配置 一、DSLS下载二、安装DLS三、使用DSLS四、更改计算机ID五、部分常见DSLS相关问题 一、DSLS下载 下载地址&#xff1a;https://software.3ds.com/?ticketST-5190987-dUM0dflc6zfjf04F5EXx-cas 注意&#xff1a;需要一个注册了的达索账号才能登录进去下载 一…

飞塔防火墙开局百篇——002.FortiGate上网配置——透明模式配置(Transparent)

透明模式配置 开启透明模式创建策略 在不改变现有网络拓扑前提下&#xff0c;将防火墙NGFW以透明模式部署到网络中&#xff0c;放在路由器和交换机之间&#xff0c;防火墙为透明模式&#xff0c;对内网网段192.168.1.0/24的上网进行4~7层的安全防护。 登陆FortiGate防火墙界面&…

24.网络游戏逆向分析与漏洞攻防-网络通信数据包分析工具-根据配置文件自动生成C语言头文件

免责声明&#xff1a;内容仅供学习参考&#xff0c;请合法利用知识&#xff0c;禁止进行违法犯罪活动&#xff01; 如果看不懂、不知道现在做的什么&#xff0c;那就跟着做完看效果 内容参考于&#xff1a;易道云信息技术研究院VIP课 上一个内容&#xff1a;23.实现配置工具…

【infiniband监控】grafana变量使用细化优化监控指标

本站以分享各种运维经验和运维所需要的技能为主 《python零基础入门》&#xff1a;python零基础入门学习 《python运维脚本》&#xff1a; python运维脚本实践 《shell》&#xff1a;shell学习 《terraform》持续更新中&#xff1a;terraform_Aws学习零基础入门到最佳实战 《k8…

Bitmap实现原理应用场景

Bitmap是什么&#xff1f; 用内存中连续的二进制位&#xff08;bit&#xff09;&#xff0c;用0或1标识数据是否存在。 长度为10的bitmap&#xff0c;1&#xff0c;2&#xff0c;3&#xff0c;4 在bitmap中存在。 Bitmap实现 1、字符串 数值对应字符串的下标、二进制位0&…

Centos7安装postgresql14步骤

1、进入网址 https://www.postgresql.org/download/ 2、按步骤执行 # Install the repository RPM: sudo yum install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-7-x86_64/pgdg-redhat-repo-latest.noarch.rpm# Install PostgreSQL: sudo yum install -y…

ST MotorControl Workbench 6.2.1 使用总结

目录 前言 软件安装 根据自己硬件配置参数 生成代码 开发板运行 ​ 总结 前言 好久没有玩ST的电机库了&#xff0c;已经更新到了MotorControl Workbench 6.2.1&#xff0c;6以上的版本比5的版本界面操作有很大的不同&#xff0c;核心算法有些增加。最近体验了一把使用自…

linux设置systemctl启动

linux设置nginx systemctl启动 生成nginx.pid文件 #验证nginx的配置&#xff0c;并生成nginx.pid文件 /usr/local/nginx/sbin/nginx -t #pid文件目录在 /usr/local/nginx/run/nginx.pid 设置systemctl启动nginx #添加之前需要先关闭启动状态的nginx&#xff0c;让nginx是未…

一款好用的AI工具——边界AICHAT(三)

目录 3.23、文档生成PPT演示3.24、AI文档翻译3.25、AI翻译3.26、论文模式3.27、文章批改3.28、文章纠正3.29、写作助手3.30、文言文翻译3.31、日报周报月报生成器3.32、OCR-DOC办公文档识别3.33、AI真人语音合成3.34、录音音频总结3.35、域方模型市场3.36、模型创建3.37、社区交…

Python 导入Excel三维坐标数据 生成三维曲面地形图(面) 4-4、线条平滑曲面(修改颜色)去除无效点

环境和包: 环境 python:python-3.12.0-amd64包: matplotlib 3.8.2 pandas 2.1.4 openpyxl 3.1.2 scipy 1.12.0 代码: import pandas as pd import matplotlib.pyplot as plt from mpl_toolkits.mplot3d import Axes3D from scipy.interpolate import griddata fro…

I2C驱动AT24C02

文章目录 一、硬件电路设备地址 二、使用步骤字节写:页写入:任意写:任意读: 一、硬件电路 设备地址 设备需要一个8位的设备地址字&#xff0c;后面跟着一个启动条件&#xff0c;以使芯片能够进行读或写操作 设备地址字由一个强制的1,0序列的前四个最有效的位&#xff0c;如所示…

(二)运行自己的stable-diffusion

前面的步骤如https://datawhaler.feishu.cn/docx/BwjzdQPJRonFh8xeiSOcRUI3n8b所示 拷贝、解压文件后&#xff0c;进入到stable-diffusion-webui的文件夹中&#xff0c;文件如下&#xff1a; 启动&#xff1a; 运行效果&#xff1a; 由于生成了好几个图&#xff0c;所以…

springboot的Converter和HttpMessageConveter

Converter和HttpMessageConveter是springboot和springmvc在处理请求的时候需要用到的。但是这两者的完全是不一样的&#xff0c;作用的地方也不一样。 1&#xff0c;springboot和springmvc处理请求的流程 先来回顾一下处理请求的流程&#xff1a; 用户向服务器发送请求&#…

Webbench,一个简单好用的web压力测试工具

Webbench 是一个简单且轻量级的Web服务器压力测试工具&#xff0c;它通过创建多个子进程来模拟多个客户端同时向服务器发送请求。运行平台是linux 安装Webbench: 1 下载Webbench源代码&#xff1a; wget http://www.ha97.com/code/webbench-1.5.tar.gz 2 解压源代码包&#…

焦点调制网络

摘要 https://arxiv.org/pdf/2203.11926.pdf 我们提出了焦点调制网络&#xff08;简称FocalNets&#xff09;&#xff0c;其中自注意力&#xff08;SA&#xff09;被焦点调制模块完全取代&#xff0c;用于在视觉中建模令牌交互。焦点调制包含三个组件&#xff1a;&#xff08;…

AIGC——ComfyUI 安装与基础使用

简介 ComfyUI是一个基于节点流程的稳定扩散操作界面&#xff0c;通过流程实现了更加精准的工作流定制和完善的可复现性。每个模块都有特定的功能&#xff0c;我们可以通过调整模块连接来实现不同的出图效果。然而&#xff0c;节点式的工作流也提高了一定的使用门槛。同时&…

一条 sql 语句可能导致的表锁和行锁以及死锁检测

锁 MDL 当对一个表做增删改查操作的时候&#xff0c;加 MDL 读锁&#xff1b;当要对表做结构变更操作的时候&#xff0c;加 MDL 写锁 ALTER TABLE tbl_name NOWAIT add column ... ALTER TABLE tbl_name WAIT N add column ... …

Deep Q-Networks(DQN)

Deep Q-Networks&#xff08;DQN&#xff09;是一种将深度学习技术与Q学习算法相结合的强化学习方法。通过使用深度神经网络来近似Q函数&#xff0c;DQN能够有效地处理具有高维状态空间的复杂问题&#xff0c;这在传统的Q学习方法中是难以实现的。DQN的提出标志着强化学习在处理…

jeecg 启动 微服务 更改配置本地host地址

1. windows系统下&#xff0c;在开始—运行里面输入(找不到运行菜单可直接按WinR键)&#xff1a; C:\WINDOWS\system32\drivers\etc &#xff0c;如图所示&#xff1a; 2. 用记事本 打开这个文件 在最下面输入这个即可