c++复习笔记——异常处理机制
Posted 努力学习的少年
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了c++复习笔记——异常处理机制相关的知识,希望对你有一定的参考价值。
- 💂 个人主页:努力学习的少年
- 🤟 版权: 本文由【努力学习的少年】原创、在CSDN首发、需要转载请联系博主
- 💬 如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连)和订阅专栏哦
目录
一. 异常处理
1. 基本思想
事实上,所谓的异常就是程序在运行过程中,由于使用环境的变化及用户的操作而产生的错误,因此异常可以被认为是错误的侠义概念。异常处理是在程序设计过程中,针对可预测的异常编制相应的预防代码,防止程序直接崩溃掉。
一个应用程序,既要保证其正确性,还应该有容错能力,如果用户正确操作时,能正常运行,并且在应用环境出现意外或用户操作不当,有合理的反应。c++异常的处理的目的是在异常发生时,尽可能地减少破坏,尽量少影响程序其他部分的运行,如果是用c的assert方法,那么就会整个程序都会崩溃,抛异常是一种调试的一种机制。
2. 抛出异常
当一段程序中发现错误数据,但是该程序不知道如何处理时,可以抛出异常。在c++中,使用throw来抛出异常。抛出异常的语法如下:
throw 错误信息;
错误信息可以是任意类型,如int类型,string类型等。
当用户使用throw语句抛出异常的时,可以避免程序出现运行错误。但是如果只有异常抛出,程序就会中途退出,这是具体程序设计不被允许的,为此c++引入了异常的捕获和处理机制。
2. 捕获异常
如果一个函数抛出一个异常,他必须假定该异常能被捕获和处理,在c++中,提供了语句try....catch语句来捕获异常,其中try和catch分别用于定义异常和i的定义异常处理。
- 定义异常时将可能产生错误的语句放在try语句块中.
- 定义异常处理是将异常处理的语句放在语句块中,以便异常被传递来时处理.
try后面至少有一个catch语句块,一旦某个异常被抛出,异常处理机制将会按照书写的catch语句块的代码从上到下找到匹配的语句,一旦找到一个相匹配语句,则像调用函数一样进入该处理器中进行处理。
二. 异常的抛出和匹配规则
- 异常是通过抛出异常对象而引发的,该对象的类型决定了应该激活哪个catch的处理代码,如下,如果抛出的对象是string,那么就会找到catch中的异常类型声明为string的语句块,如果抛出的对象是int类型,那么就会找到catch中异常类型声明为int语句块。
- 抛出异常后,抛出异常语句会直接跳转到处理异常的语句 ,抛出异常后面的语句不会被执行,func2函数不会被调用。处理异常后,执行catch之后的语句。
测试结果:
- 抛出异常对象后,会生成一个异常对象的拷贝,因为抛出的异常对象可能是一个临时对象,所以生成的一个拷贝对象,这个拷贝对象catch执行语句块后就会被销毁.(类似函数的道理)
- catch(...)可以捕获任意类型的异常。
异常调用链中异常展开栈匹配规则
抛出的异常如果在当前的函数战阵中没有找到相匹配的catch语句,那么就会依次根据函数调用的路线进行返回上一层函数栈,继续查找catch语句。如果上一层找到匹配的catch语句,那么就处理该异常,那么上上一层的函数就认为没有异常,如果找不到相匹配catch语句,那么会继续返回到上一层函数栈,直到main函数栈,依旧没有匹配,那么则终止程序。所以实际中我们最后都要加一个catch(...)捕获任意类型的异常,否则当有异常没捕获,程序就会直接终止。
情景一:
- func2抛出一个异常,在自己的函数栈帧中没有找到相匹配的catch语句,则返回到func1函数,在func1函数栈帧中找到了相匹配的catch语句,并处理好了异常,然后func2执行完后,返回到main函数,main函数认为没有异常,便没有捕获异常,便往后执行cout<<“ main end"<<endl;语句。
情景2:
- func2抛出一个异常,在自己的函数栈帧中没有找到相匹配的catch语句,则返回func1函数,在func1函数栈帧也没有找到了相匹配的catch语句,便直接返回到main函数中,在main函数中找到了,相对应的catch语句,处理好异常后,继续执行main函数中的语句,直到程序结束。
情景3
- func2抛出一个异常,在自己的函数栈帧中没有找到相匹配的catch语句,则返回到func1函数,在func1函数栈帧也没有找到了相匹配的catch语句,便直接返回到main函数中,main函数中也没有找到相匹配的catch语句,程序直接终止掉。
因此为了,如果防止某个函数直接被返回,可以在catch语句链中的最后加上catch(...)语句。
三. 异常规范
- 1.异常规格说明的目的是为了让函数使用者知道该函数可能抛出的异常有哪些。 可以在函数的后面接throw(类型),列出这个函数可能抛掷的所有异常类型。
- 2.函数的后面接throw(),表示函数不抛异常。
- 3.若无异常接口声明,则此函数可以抛掷任何类型的异常。
void func1()throw()//表示该函数不会抛出异常
void func2()throw(int,char,string)//表示该函数会抛出int,char,string类型的异常
四. 自定义异常体系
1.自定义异常体系
- 异常的捕获可以捕获到的异常可以是自定义的类型, 在自定义的类型中,一般都需要保包含异常编号和异常描述这两个成员变量。如下:
- 当有了自定义类型后,就可以通过异常编号判断是哪一个异常,可以通过打印errmsg知道该出现异常的原因。
如下:func1没有出现异常,此时func1在堆上已经创建好了空间,并初始化了p,如果func2函数抛出异常,那么就会直接跳转到相匹配的catch语句,func2后面的delete就没有被执行,所以就需要在处理异常那里delete p,防止程序的内存泄漏。因为处理异常后,main函数下面有可能继续执行。
- catch中包含基类的声明类型可以捕获到派生类的异常对象(派生类需要public方式继承基类).,也就是说当抛出一个派生类异常对象,包含基类的类型catch可以捕捉到它。
如下两个类,基类MyException和派生类SqlExption。
- 抛出的SqlExption异常对象被catch为MyException的类型给捕获到
实际使用中很多公司都会自定义自己的异常体系进行规范的异常管理,因为一个项目中如果大家随意抛异常,那么外层的调用者基本就没办法玩了,所以实际中都会定义一套继承的规范体系。这样大家抛出的都是继承的派生类对象,捕获一个基类就可以了。
如上:我们只要catch的MyException类型,就能捕获支链上所有的类型。
2. c++标准库
c++中提供了一套的标准异常,继承关系如下。
当我们catch中包含exception类型,就能捕获到c++库中可能抛出的所有异常。
一. 异常的优缺点
异常的优点
- 异常对象定义好了,相比错误码的方式可以清晰准确的展示出错误的各种信息,甚至可以包含堆栈调用的信息,这样可以帮助更好的定位程序的bug。
- 很多测试框架都使用异常,这样能更好的使用单元测试等进行白盒的测试
- 很多的第三方库都包含异常,比如boost、gtest、gmock等等常用的库,那么我们使用它们也需要使用异常。
异常缺点
- 异常会导致程序的执行流乱跳,并且非常的混乱,并且是运行时出错抛异常就会乱跳。这会导致我们跟踪调试时以及分析程序时,比较困难。
- . 异常尽量规范使用,否则后果不堪设想,随意抛异常,外层捕获的用户苦不堪言。所以异常规范有两点:一、抛出异常类型都继承自一个基类。二、函数是否抛异常、抛什么异常,都使用 func() throw();的方式规范化。
- C++标准库的异常体系定义得不好,导致大家各自定义各自的异常体系,非常的混乱。
总的来说,异常的利是大于弊的。
以上是关于c++复习笔记——异常处理机制的主要内容,如果未能解决你的问题,请参考以下文章