Last Updated on 2022年9月30日
异常
- C++内所说的异常是指:可以预见的非正常状况,例如输入的指针为空;而非不可预见的问题,例如突然停电,或者突然被用户把进程kill掉,并不是C++需要处理的"异常"
- 异常特性会导致程序的执行流程不可控,且往往对OS及runtime有一定的要求(可移植性差),所以没有特殊需求时,不应当使用这个特性.
throw-try-catch
是异常系统的典型三个环节.- throw出的可以是任意对象,只要catch处声明的对象可以用throw的对象初始化即可.习惯上我们会专门设计一个类,因为自定义类可以承载更多的信息
- 异常catch中的形参可以使用引用,以使用多态机制
- 异常抛出后寻找catch的过程称为栈展开,被展开的函数栈内所有局部对象都将被销毁,因此抛出的异常对象必须不依赖局部对象.
- 标准库内提供了以
exception
为基类的若干异常,我们可以使用这个类,也可以自定义类,该类的const char * what()
成员用于给用户提供信息.- catch时,优先使用引用, 从而保证能派生类实例能绑定到基类参数上.
- 构造函数内可以throw异常;析构函数若抛出异常,则会直接ternimate.
- catch的匹配顺序是确定的,第一个能初始化的catch将用于匹配,而非最优的那个.
- 匹配过程中仅允许非const到const,数组到指针,派生类到基类这种转换.
- 处理异常时,可以处理一半再然后通过
throw
再次将其抛给上层. - 一般而言,对于一个函数,当它碰到非正常状况时(例如空指针,或者内部函数抛出异常),其异常安全性要求有三个层次:
- 低:该函数将异常抛出,该函数的输入对象都可以正常析构,不存在内存泄漏.
- 中:该函数将异常抛出,且恢复相关对象到调用该函数前的原状,仿佛什么都没发生过.
- 高:该函数保证不会抛出异常,换言之,该函数能自己处理所有可能的错误,从而保证总是能正常return.(当然,正常return不意味着结果和你预期相同, 例如,可以return一个错误码给调用者)
- 健壮的,不抛出异常的swap()是高等级异常安全的重要工具.这样就可以使用
copy-修改-swap
的流程来保证异常安全. - noexcept
- 可以用于标记函数是否抛出异常
- noexcept的函数和普通函数是不同的,需要使用不同的函数指针.
- 编译器一般提供了trait,可以在编译期求值,判断一个函数是否抛出异常
- 对于
std::vector<MyType>
等场合,仅当MyType的移动构造/移动拷贝为noexcept
时,vector整体才能支持移动,否则将会默认使用拷贝.