目录
C++为什么要引入异常?
什么是异常?
怎么使用异常处理错误?
异常的抛出规则:
异常的匹配规则:
单个catch语句,不能完全处理掉异常?重新抛出异常
什么是异常安全问题?
什么会导致异常安全问题?
异常规范有何作用?怎么做?
何为异常体系?
总结C++异常的优缺点
优点
缺点
C++为什么要引入异常?
首先看C语言传统处理错误的方式,主要有两种:
1.用assert,当不满足条件的时候直接终止程序,缺陷是用户难以接受
2.返回错误码,很多系统调用都是把错误码放在了errno中,缺点是需要程序员自己去用错误码获取错误信息,比如strerror(errno)
为了更好地处理错误,C++引入异常。
什么是异常?
异常:异常是一种处理错误的方式,当函数发现自己无法处理错误的时候,就可以抛出异常,函数的直接调用者或者间接调用者可以捕获这个异常,从而去处理这个错误。
怎么使用异常处理错误?
C++处理异常的三个关键字
throw catch try
语法格式:
try
{
// 保护的标识代码
}catch( ExceptionName e1 )
{
// catch 块
}catch( ExceptionName e2 )
{
// catch 块
}catch( ExceptionName eN )
{
// catch 块
}
异常的抛出规则:
- 抛出的对象类型决定了哪一个catch语句捕获异常
- 处理代码是与抛出类型匹配,而且离抛异常位置最近的那一块
- 抛出异常对象后,因为抛出的异常对象可能是一个临时的对象,所以捕获异常的语句会生成一个拷贝的对象,在catch语句之后就会销毁这个拷贝的对象
- catch(...)可以捕获任意类型的异常
- 可以抛出派生类对象使用基类类型捕获
异常的匹配规则:
- 首先检查本身是否在try语句块内部,如果是就查找相应的catch语句,如果有匹配就在其地方处理
- 然后没有匹配到catch则退出当前的函数栈,在调用函数的栈中查找匹配的catch
- 当达到主函数main的栈还没有匹配成功时,就会终止程序
- 找到匹配的catch语句并处理之后,就会往catch语句后面继续执行
简单代码理解一下调用函数栈:
#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
using std::cout;
using std::endl;
void fun2()
{
throw 100;
cout << "fun2" << endl;
}
void fun1()
{
fun2();
}
int main()
{
try {
fun1();
}
catch (int a)
{
cout << "捕获异常a:" << a << endl;
}
catch (...)
{
printf("捕获任意类型的异常\n");
}
return 0;
}
输出:
单个catch语句,不能完全处理掉异常?重新抛出异常
- 当单个catch语句,不能完全处理掉异常,在经过一些处理之后想交给外层调用的函数再来处理,catch语句块中就可以通过throw;重新抛出异常传递给更上层的函数处理异常
什么是异常安全问题?
由于抛异常导致的资源安全问题
什么会导致异常安全问题?
构造函数中抛出异常,可能导致对象没有完全初始化或者对象不完整
析构函数完成资源的清理,最好不要在其中抛出异常,否则可能导致资源泄露
在new和delete当中抛出异常导致内存泄露,在lock和unlock之间抛出异常导致死锁,C++用来RAII 思想来解决上述问题
异常规范有何作用?怎么做?
目的是为了让函数的使用者知道有可能抛出的异常有哪些类型,在函数后面加(类型),列出函数可能抛出的所有异常的类型
函数后面接throw()表示不抛异常
如果没有异常接口的声明,则此函数可以抛任何类型的异常
C++新增关键字noexcept表示不会抛异常
何为异常体系?
简单而言是一套继承体系,子类直接或间接派生自同一基类。
自定义异常体系:制订一套继承规范体系,大家都抛出的是继承于同一个基类的派生类对象,只要捕获基类类型就可以了
C++标准库的异常体系
总结C++异常的优缺点
优点
- 相比于错误码的方式,可以更清晰的展示出错误信息,可以更好的定位错误
- 返回错误码的方式在函数调用链当中得层层返回错误,最外层才能拿到错误,但是抛异常则可以一次性跳到最外层
- 使用许多的第三方库需要使用异常
- 部分函数使用异常更好处理,比如T& operator这样的不确定返回值类型的函数
缺点
- 抛异常可能会导致执行流乱跳,导致执行流非常混乱,使跟踪调试分析程序的时候比较困难
- C++没有垃圾回收机制,有了异常之后,可能导致资源泄露等问题,即异常安全问题需要使用RAII来处理资源管理问题,有一定学习成本
- C++标准库异常体系定义的不好,自定义的异常体系较多,较为混乱