C++异常剖析

什么是异常?

        在程序运行的过程中,我们不可能保证我们的程序百分百不出现异常和错误,那么出现异常时该怎么报错,让我们知道是哪个地方错误了呢?
        C++中就提供了异常处理的机制。

一、异常处理的关键字

(1)throw: 当问题出现时,程序会抛出一个异常。这是通过使用 throw 关键字来完成的。
(2)catch: 在您想要处理问题的地方,通过异常处理程序捕获异常。catch 关键字用于捕获异常。
(3)try:try 块中的代码标识将被激活的特定异常。它后面通常跟着一个或多个 catch 块(至少要跟一个)。

        当被try的括号包围的代码块(我们可称之为保护代码)出现异常时,就会抛出异常,catch块的代码就会捕获异常并执行catch包围的代码块。因为异常也可以有不同的种类,所以我们可以在try后面跟着多个catch块,捕获不同的异常,如下:

try
{// 保护代码
}
catch( ExceptionName1 e1 )//第一种异常
{// catch 块
}
catch( ExceptionName2 e2 )//第二种异常
{// catch 块
}
catch( ExceptionName3 eN )//第三种异常
{// catch 块
}

二、捕获异常

        使用 throw 语句可以在代码块中的任何地方抛出异常。
        throw 语句的操作数可以是任意的表达式,表达式的结果的类型决定了抛出的异常的类型。
如下:

        可以看到,我们抛出的是一串字符串,所以编译器报错的信息说异常类型是char(其实是const char*,但它简写了)。

        实际我们编程的时候,我们肯定不希望编译器就只给我说异常类型是char*就完了。我们还希望把它打印出来,让我们看得到"出现了除数为0的错误",这就需要捕获异常。

#include <iostream>
using namespace std;
double division(double a, double b)
{try{if (b == 0){throw "出现了除数为0的错误";//throw关键字抛出错误类型 const char*}}catch (const char* error_string)//捕获const char*类型的错误{cout << error_string << endl;//打印它}return (a / b);
}
int main()
{cout << division(1, 0);
}

        可见,有了try和catch后,程序不会在遇到错误时崩溃终止,而是会处理它,然后继续运行。
所以这个程序依然能输出计算结果inf(无限大),但是在此之前也打印了报错信息。
总之,利用异常处理机制,我们就可以在程序运行时处理异常,大大减少程序崩溃的概率。
你在生活中经常遇到软件闪退的问题,十有八九没有做好异常处理。

三、C++标准库自带的异常类型

下表是对上面层次结构中出现的每个异常的说明:

std::exception 该异常是所有标准 C++ 异常的父类。 std::bad_alloc 该异常可以通过 new 抛出。
std::bad_cast 该异常可以通过 dynamic_cast 抛出。 std::bad_typeid 该异常可以通过 typeid
抛出。 std::bad_exception 这在处理 C++ 程序中无法预期的异常时非常有用。
std::logic_error 理论上可以通过读取代码来检测到的异常。
std::domain_error 当使用了一个无效的数学域时,会抛出该异常。
std::invalid_argument 当使用了无效的参数时,会抛出该异常。
std::length_error 当创建了太长的std::string 时,会抛出该异常。
std::out_of_range 该异常可以通过方法抛出,例如 std::vector 和std::bitset<>::operator。
std::runtime_error 理论上不可以通过读取代码来检测到的异常。
std::overflow_error 当发生数学上溢时,会抛出该异常。
std::range_error 当尝试存储超出范围的值时,会抛出该异常。
std::underflow_error 当发生数学下溢时,会抛出该异常。

四、异常规格说明

异常规格说明的目的是为了让函数使用者知道该函数可能抛出那些异常,可以再函数的声明中列出这个函数可能抛出的所有异常类型,例如:

viod fun() throw(A,B,C,D);

        throw后面的括号内,必须写出会抛出的异常的类型,如果括号里面是空的,说明这个函数不会抛出任何异常。
        如果你不声明throw(A,B,C,D),那么编译器就会认为这个函数什么异常都可能会抛出。
        异常规范声明可以让编译器知道可能会抛出的异常有哪些,就可以提升编译速度和给编译器更多的优化空间,让你的代码性能更高,也可以让其他程序员了解你这段代码可能会产生的异常,提高代码可读性。

五、noexcept关键字

在C++11中新增了noexcept关键字以表示这个函数不会抛出某种异常。并且可以阻止异常的传播。

无条件的noexcept关键字

当我们声明的noexcept关键字无条件时,表示这个函数中所的所有代码都不会产生异常,如下:

void fun() noexcept; //C++11
void fun() noexcept(); //也可以写成这样,等价的
void fun() noexcept(...); //也可以写成这样,等价的
void fun() noexcept(true); //也可以写成这样,等价的
有条件的noexcept关键字
void fun(Type& x, Type& y) noexcept(noexcept(noexcept(fun1()),noexcept(fun2()));
//表示fun函数内会调用到的fun1函数和fun2函数都不会抛出异常,但不保证其他代码不会抛出异常

什么时候我们需要noexcept关键字?
        使用noexcept表明函数或操作不会发生异常,会给编译器更大的优化空间。然而,并不是加上noexcept就能提高效率。
以下情形鼓励使用noexcept:

(1)移动构造函数(move constructor)
(2)移动分配函数(move assignment)
(3)析构函数(destructor)。这里提一句,在新版本的编译器中,析构函数是默认加上关键字noexcept的。下面代码可以检测编译器是否给析构函数加上关键字noexcept。
(4)叶子函数(Leaf Function)。叶子函数是指在函数内部不分配栈空间,也不调用其它函数,也不存储非易失性寄存器,也不处理异常。
最后强调一句,在不是以上情况或者没把握的情况下,不要轻易使用noexcept。

六、定义新的异常类型

只有C++标准库自带的异常类型肯定是不够用的,我们实际工作中还需要根据项目需求定义新的异常类型。

#include <iostream>
#include <exception>
using namespace std;
class DivisionZeroException :public exception//基于exception类定义新的异常类型除以0导致的异常类
{
public://这里重载了父类的虚函数what()//throw ()的括号里面没有东西,这表示这个函数不会抛出任何异常//const 是常量的关键字,常量在定义后无法被修改const char* what() const throw (){return "出现了除以0的错误\n";}
};
double division(double a, double b)
{try{if (b == 0){DivisionZeroException e;throw e;//throw关键字抛出错误类型DivisionZeroException}}catch (DivisionZeroException e)//捕获const char*类型的错误{cout<<e.what();//打印什么异常了}return (a / b);
}
int main()
{cout << division(1, 0);
}
出现了除数为0的错误
inf

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

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

相关文章

Matlab simulink PLL学习笔记

本文学习内容&#xff1a;【官方】2022小迈步之 MATLAB助力芯片设计系列&#xff08;一&#xff09;&#xff1a;电路仿真与模数混合设计基础_哔哩哔哩_bilibili 时域模型 testbench搭建 菜单栏点击simulink 创建空白模型 点击库浏览器 在PLL里面选择一种架构拖拽到画布。 如…

Hibernate 框架 (2023年架构师下半年案例分析题)

Hibernate 是一种对象和关系之间映射的框架&#xff0c;是 Java 应用和关系数据库之间的桥梁。它可以将数据库资源映射为一个或者多个 POJO。将面向数据库资源的各种业务操作以 POLO 的属性和方法的形式实现&#xff0c;使人们摆脱烦琐的 JDBC 代码&#xff0c;将精力更多地集中…

P1035 [NOIP2002 普及组] 级数求和题解

题目 已知&#xff1a;.显然对于任意一个整数 k&#xff0c;当 n 足够大的时候,Sn​>k。 现给出一个整数k&#xff0c;要求计算出一个最小的n&#xff0c;使得Sn​>k。 输入输出样例 输入 1 输出 2 代码 #include<iostream> using namespace std; int mai…

【matlab程序】matlab画螺旋图|旋转图

%% 数学之美====》螺旋线 % 海洋与大气科学 % 20231205 clear;clc;close all; n=10; t=0:0.01:2pin; R=1; xx=nan(length(t),1);yy=nan(length(t),1); for i=1:length(t) xx(i)=Rcos(t(i)); yy(i)=Rsin(t(i)); R=R+1; end figure set(gcf,‘position’,[50 50 1200 1200],‘col…

中断、异常和系统调用(2-1,2-2,2-3)

2-1 课堂练习2.1&#xff1a;外部中断 本实训分析 Linux 0.11 对外部中断的响应和处理过程。在每条指令执行的末尾&#xff0c;如果没有关中断&#xff0c;CPU 会检查是否收到了外部中断信号&#xff0c;如果有信号&#xff0c;则 CPU 就切换到核心态去执行对应的中断处理程序…

推荐5款让人安心的软件,大胆分享给别人吧

​ 今天我要分享五款神器软件&#xff0c;它们都是各领域的佼佼者&#xff0c;能让你的生活和工作更好。请大家放心地用它们&#xff0c;也告诉别人它们的好处。请看下面的介绍吧。 1.文件加密——Cryptomator ​ Cryptomator是一款文件加密软件&#xff0c;可以让用户对文件…

中伟视界:皮带跑偏、异物检测AI算法除了矿山行业应用,还能在钢铁、火电、港口等行业中使用吗?

随着工业化的发展&#xff0c;皮带输送机已经成为各行业中不可或缺的重要设备&#xff0c;但是在使用过程中&#xff0c;由于各种原因&#xff0c;皮带常常出现跑偏问题&#xff0c;给生产运营带来了诸多困扰。不仅仅是矿山行业&#xff0c;钢铁、火电、港口等行业也都面临着皮…

Web前端 ---- 【Vue】Vuex的使用(辅助函数、模块化开发)

目录 前言 Vuex是什么 Vuex的配置 安装vuex 配置vuex文件 Vuex核心对象 actions mutations getters state Vuex在vue中的使用 辅助函数 Vuex模块化开发 前言 本文介绍一种新的用于组件传值的插件 —— vuex Vuex是什么 Vuex 是一个专为 Vue.js 应用程序开发的状态…

SQL Server——权限管理

一。SQL Server的安全机制 SQL Server 的安全性是建立在认证和访问许可两种安全机制之上的。其中&#xff0e;认证用来确定登录Sal Server 的用户的登录账户和密码是否正确&#xff0e;以此来验证其是否具有连接SQL Server 的权限;访问许可用来授予用户或组能够在数据库中执行哪…

C语言精选——选择题Day39

第一题 1. 有下面的定义&#xff0c;则 sizeof(s) 为多少&#xff1f; char *s "\ta\017bc"; A&#xff1a;9 B&#xff1a;5 C&#xff1a;6 D&#xff1a;7 答案及解析 C 本题涉及到了转义字符 \t 是水平制表符&#xff0c;算一个字节 \017 是表示八进制数&#…

整合消息队列RabbitMQ

为什么使用消息队列MQ&#xff1f; 因为使用消息队列有多个好处&#xff1a;可以实现系统服务的解耦、异步和削峰&#xff1a; 异步通信&#xff1a;消息队列提供了一种异步通信的方式&#xff0c;发送方可以将消息发送到队列中&#xff0c;然后继续执行其他任务&#xff0c;…

《Linux源码趣读》| 好书推荐

目录 一. &#x1f981; 前言二. &#x1f981; 像小说一样趣读 Linux 源码三. &#x1f981; 学习路线 一. &#x1f981; 前言 最近、道然科技给狮子送了两本书&#xff1a;一本是付东来的《labuladong的算法笔记》、一本是闪客著的《Linux源码趣读》&#xff0c;《labulado…