0 结论
- 只要函数不会发射异常,就为其加上
noexcept 声明【接口中的组成部分】,调用方可能会对其产生依赖 noexcept 性质对于移动凑走、swap、内存释放和析构函数最有价值
这样的影响:
- 带有
noexcept 可以获得更多优化的机会,进而提高运行效率 - 影响可以调用代码的异常安全性
2 提高运行效率
noexcept 可以让编译器生成更好的目标代码。因为在带有noexcept 声明的函数中,优化器不需要在异常传出函数的前提下,将执行期栈保持可开解状态(栈展开:在运行时期间从函数调用栈中删除函数实体。如果异常没有在抛出它的函数中被处理,则会激活栈展开。),也不需要异常逸出函数的前提下,保证其中的对象以其被构造顺序的逆序完成析构。
而以"throw()"异常规格声明的函数就享受不到noexcept 带来的灵活优化。
3 异常安全保证
例如,移动操作,在C++98中,std::vector 保持着强异常安全性,push_back操作是先将就内存复制到新内存,才将就内存中的对象析构。如果在复制元素过程中抛出异常,std::vector 就会保持原样不变。
而在C++11中,对std::vector 类型的对象元素复制操作部分换成了移动操作,这样的后果是不仅提升了效率,但是也面临着如果移动第n+1个元素抛出异常,则push_back操作就无法完成,也无法恢复到原始状态。C++11对于std::vector::push_back 使用的就是“能移动则移动,必须复制时才复制”,类似的还有(std::vector::reserve ,std::deque::insert 等)。对于使用移动操作的都声明为noexcept ,来说明该移动操作不会发射异常。
4 使用
- 只有在保证函数实现长期具有
noexcept 性质的前提下,才给予其noexcept 声明(不要故意扭曲函数实现,使之符合noexcept 性质,造成主次不分); - 异常中立的函数永远不具备
noexcept (异常中立函数:自身并不抛出异常,但是它们调用的函数可能会发射异常,当异常发生时,会把异常经由它们传至调用栈的更深一层); - 一般把
noexcept 保留给带有宽松契约的函数
- 宽松契约:没有前置条件(函数中对形参数做出要求,当不满足时会抛出异常),无需关心函数状态
- 狭隘契约:如果前置条件被违反,则结果成未定义的
- 默认的,内存释放函数和所有析构函数(无论是否是用户定义,还是编译器生成)都隐式地具备
noexcept 性质。
|