14只要函数不会发出异常,就为其加上noexcept声明:
首先,如果知道一个函数不可能抛出异常,则应该将其声明为noexcept,以提高代码的运行效率,但是如果函数却违法声明抛出了异常将导致程序直接中止。
noexcept
可以让编译器生成更好的目标代码。因为在带有noexcept
声明的函数中,优化器不需要在异常传出函数的前提下,将执行期栈保持可开解状态(栈展开:在运行时期间从函数调用栈中删除函数实体。如果异常没有在抛出它的函数中被处理,则会激活栈展开。),也不需要异常逸出函数的前提下,保证其中的对象以其被构造顺序的逆序完成析构。而以"throw()"异常规格声明的函数就享受不到noexcept
带来的灵活优化。
而使用noexcept而非throw()好处有以下几个原因:
(1)声明为nocept,优化器会对函数做最大值的优化,包括将执行器栈保持在可开解状态。
(2)C++11新增了移动语义,所以STL标准库也做了一次大的性能调整,即在原先赋值操作地方,将采用"能移动则移动,必须复制才复制"策略。而能否移动的一个关键点就是STL函数是否声明了noexcept。而STL函数是否声明noexcept,取决于STL容器所含自定义对象对应的函数是否声明了noexcept。例如:如果自定义对象的移动拷贝函数声明了noexcept,则在对该对象进行push_back后扩张时,将使用移动拷贝而非复制拷贝,又或者对某对象的swap()成员函数声明了noexcept,则容器在调用STL的swap()函数时,将使用移动拷贝。
(3)默认的,所有内存释放函数(delete函数)或析构函数,无论是用户自定义还是编译器生成,都隐式的声明了noexcept。
下列是微软对于noexcept建议的使用:
某些类型的操作绝不会导致异常。 它们的实现应是可靠的,并且应该正常处理可能的错误情况。 它们不应使用异常来指示故障。 此规则标记此类操作未显式标记为 noexcept
的情况,这意味着它们可能会引发异常,并且无法传达有关其可靠性的假设。
- 特殊类型的操作:
- 析构函数;
- 默认构造函数;
- 移动构造函数和移动赋值运算符;
- 具有移动语义的标准函数:
std::move
和std::swap
。
struct S
{
S() noexcept {} //默认构造
~S() noexcept {} //析构
S(S&& s) noexcept {/*impl*/} //移动构造
S& operator=(S&& s) noexcept {/*impl*/} //移动赋值
S(const S& s) noexcept {/*impl*/}
//拷贝构造
S& operator=(const S& s) noexcept {/*impl*/} //拷贝赋值
};
总结:
- 只要函数不会发射异常,就为其加上
noexcept
声明【接口中的组成部分】,调用方可能会对其产生依赖 noexcept
性质对于移动操作、swap、内存释放和析构函数最有价值- 带有
noexcept
可以获得更多优化的机会,进而提高运行效率 - 影响可以调用代码的异常安全性
Comments NOTHING