Item14 Declare functions noexcept if they won't emit exception

来源:互联网 发布:vmware共享文件夹linux 编辑:程序博客网 时间:2024/05/12 03:07

这个系列的文章来自于Effective Modern C++的读书笔记,我抽取了其中比较重要的,不容易理解的,平常我们开发过程中也不太在意的一些Item进行分析。

​ 在C++98中异常规格说明是一个废弃的,没有人用的特性,因为它需要知道一个函数可能会抛出的异常类型,如果函数后面有改动,那么异常规格说明也有可能需要改动。与此同时调用这个函数的代码也有可能需要发生相应的改变。最要命的是编译器并不负责保证上面三者之间的一致性。因此对大多数程序员来说,异常规格说明是个弱爆了的feature不值得去使用。

​ 在C++11中对于异常规格说明有了新的认识,它只标识一个函数是否会抛出异常,这也是大多数人所关心的,并且引入了新的关键字noexecpt,来表示一个函数不会抛出异常。其实C++98也可以使用throw()来表示不会抛出异常。C++11又为何会选择添加一个新的关键字呢?,虽然C++98也可以实现标识一个函数是否会抛出异常,但是其对抛出异常时的处理方式和C++11有很大的区别,C++11为了兼容所以引入了新的关键字noexcept

int f(int x) throw()    // C++98 styleint f(int x) noexcept   // C++ 11 style

​ 使用throw()来表示不抛出异常的情况下,如果函数抛出异常会导致栈解旋,然后程序结束。而使用noexcept()的情况下,程序会直接结束。这两种形式对最后生成的代码有很大的影响。对于后者就不需要生成一些用于保护堆栈的代码了,因为不需要解旋,所以对代码优化友好。

​ 上文说到大多数人比较关心函数是否会抛出异常,而不是特别关心会抛出哪些异常。这很大程度上是因为在C++中有很多地方看似并不为抛出异常,一旦抛出异常将会产生致命的影响,因此知道哪些函数会抛出异常,哪些不会抛出异常,对于我们开发程序来说会有很大的帮助,可以帮助我们写出异常安全的函数。比如:std::vector向里面追加元素的时候可能会因为为内存不足而抛出异常,在C++98中会先试图分配空间,如果空间分配成功才会拷贝原来空间中的元素到新的空间中,因此即使内存分配失败抛出异常,也不会影响vector本身,起到了异常安全的保证,而到了C++11,为了优化vector内部的元素拷贝,C++11是将原来空间中的元素一个个move到新的空间中,如果在move的过程中发生了异常,那么这将是一个不可逆的过程,并且失去了C++98中异常安全保证的能力,因此C++11需要知道move操作是否是一个不会抛出异常的操作,如果是才会使用move,这会带来性能上的提升,否则就降级到C++98的场景下,通过拷贝来实现,保证异常安全。如果move 是用了noexcept关键字声明的,那么就可以使用标准库提供的noexcept()函数来检测move是否会抛出异常,而这个在C++98中是做不到的。

​ 对于某些函数来说,noexcept很重要,对于这些函数来说noexcept是默认的,在C++98 中需要显示的指定,而C++11则上升到语言层面了,直接是默认的,默认情况下所有的内存分配和所有的析构函数,无论是用于自定义的还是默认的都是隐式的noexcept

0 0
原创粉丝点击