C++第十五讲:异常
C++第十五讲:异常
- 1.异常的概念和使用
- 1.1异常的抛出和捕获
- 1.2异常抛出和捕获的注意事项
- 1.3异常的重新抛出
- 1.4异常规范
- 2.标准库的异常
1.异常的概念和使用
1.1异常的抛出和捕获
异常的抛出和捕获的过程分为三个阶段:
1.异常的抛出:
使用throw对异常进行抛出,异常抛出之后,该函数会直接中断运行,该函数内部的对象(new出的对象仍然需要自己delete释放)会调用对应的析构函数或被系统回收,抛出的对象可能是一个局部对象,所以抛出的是对象的拷贝,该拷贝会在catch子句后销毁
2.栈展开:
异常抛出之后,开始进行栈展开过程,该过程其实就是逐层向上,检查每个函数是否有对应的catch进行捕获,这个过程中,如果一个函数中没有对应的catch进行捕获,那么该函数就会被销毁,对应的局部变量会被回收(如果没有被new出来就被跳过,肯定不需要也不允许回收),然后继续向上查找对应的catch,如果没有找到,那么就会发生报错(异常抛出之后,是必须要进行catch的)
3.异常的捕获:
使用catch对异常进行捕获,一旦捕获成功,栈展开立刻停止,我们可以通过访问异常对象对错误信息进行处理,catch后面的代码正常执行
1.2异常抛出和捕获的注意事项
1.异常会被捕获在调用链中与该对象类型匹配而且离抛出位置最近的那一个
2.重点:派生类抛出的异常,可以由基类进行捕获
3.重点:new对象必须要手动delete进行资源释放
4.catch的匹配允许权限的缩小匹配:从非常量到const常量的转换,从数组向数组指针类型的转换(一般不会抛出数组),从函数向函数指针的转换,从派生类向基类的转换(这个是重点)
5.重点:如果到main函数,异常还是没有匹配,为了防止程序非严重错误终止,通常会使用catch(…)对所有异常进行捕获
2.重点:派生类抛出的异常,可以由基类进行捕获
3.重点:new对象必须要手动delete进行资源释放
5.重点:如果到main函数,异常还是没有匹配,为了防止程序非严重错误终止,通常会使用catch(…)对所有异常进行捕获
1.3异常的重新抛出
有时catch到异常之后,要对异常进行分类,有的异常需要进行特殊的处理,就可以使用throw对异常进行重新抛出:
当我们使用微信时,可能会遇到信息发送不出去的问题,该问题可能是因为没有对方的好友,可能是网络信号不好,如果是网络不好,我们可以通过throw对信息进行重复发送(比如微信的转圈圈,其实就是消息没有发送出去,进行重复发送),重复发送三次,如果还是发送不出去的话,那么再报异常:
1.4异常规范
对于我们而言,提前知道程序是否会抛出异常很好用
1.C++98标准,函数后面加上throw(),表示可能不抛异常,后面加throw(类型1,类型2……)表示可能抛多种异常(没有强制要求使用,所以一般不使用,了解即可)
2.C++11进行了简化,函数后加noexcept表示不会发生异常,啥也不加表示可能会有异常,如果加上了noexcept,但是函数内有异常处理机制,会造成未定义行为(包括但不限于程序崩溃、数据损坏、不正确的结果输出,或者程序继续执行但表现出不可预测的行为),但是编译会编译成功
3.我们可以使用noexcept(expression)检测一个表达式是否会抛出异常,不可能抛出异常,返回true,否则,返回false
// C++98
// 这⾥表⽰这个函数只会抛出bad_alloc的异常
void* operator new (std::size_t size) throw (std::bad_alloc);
// 这⾥表⽰这个函数不会抛出异常
void* operator delete (std::size_t size, void* ptr) throw();
// C++11
size_type size() const noexcept;
iterator begin() noexcept;
const_iterator begin() const noexcept;
2.标准库的异常
C++标准库中也有自己的一套异常继承体系库,基类是exception,使用时在主函数直接捕获exception即可,我们也可以对派生类what进行重写