c++11 noexcept关键字作用

c++11 noexcept关键字作用

c++2.0中,一条经典的规范是:尽可能地为一个函数加上noexcept声明,意味着程序员向编译器保证该函数不会发射异常。

这条规范说的很对。那么,本文我们主要来探讨:

  • 为什么给函数加上noexcept会优化其性能?
  • noexcept的常见用法?
  • 你可能会觉得自己也不确定这个函数是否会在运行时发射异常,那么到底什么时机应该为函数加上noexcept可以获得最佳优化?

一、noexcept声明对函数性能的优化

这个原因从直观上理解应该是:既然开发者确保此函数不会发射异常,那么编译器也就没有必要为处理这个”可能“发生的异常添加一些事先预备好的目标代码,这在一定程度上减少了函数编译后生成的目标代码。

二、 noexcept的常见用法及注意事项

c++11和c++98对阻止异常抛出的写法不一样:

void func()throw(){}//c++98,这种写法并不能完全享受到上面提到的那种优化
void func()noexcept{}//c++11

在c++11中,noexcept的用法如下:

void func()noexcept{}//1. 
void func()noexcept(express){}//2. 

第二种写法将根据表达式的真假来决断函数是否发射异常,noexcept等价于noexcept(true)。

需要注意的是,如果承诺了func函数是不会抛出异常的,那么必须保证func调用的其他函数也是不会抛出异常的,否则无法保证func的noexcept性质。因此,我们可以百分百确定一个函数不会发射异常的情况是比较少见的!需要了解的是,c++11为所有类的析构函数都加上了“隐式”noexcept声明。

另外,假设我们承诺的noexcept函数在运行时真的发射了异常会怎么样呢?(一定要注意,如果使用了catch子句捕获并处理了异常,那不算发射异常!)我在GNU下测试过,运行时还是会抛出异常然后程序崩溃。只是在编译生成目标代码时做了优化而已。

void func()noexcept{
    int* a=nullptr;
    *a=2;//抛出一个异常 
}

bash输出:

zkcc@LAPTOP-OHBI7I8S:~/mytest$ g++ test_cast.cc -o test_cast && ./test_cast
Segmentation fault

三、为函数加上noexcept声明的最佳时机

直接给出建议:当你为一个类设计移动系列函数时,如果可以,最应该为其加上noexcept,以便此类在使用标准库容器时可以用移动操作来代替拷贝。

咋回事呢?移动操作加上noexcept与标准库容器的移动优化有啥关系呢?

以vector为例,很多标准库容器需要特定时机的扩容操作:把元素从旧内存拷贝到新开辟的内存,再析构旧内存中的元素。

针对这种情况明显可以用移动操作来优化之。(为什么移动操作比拷贝操作速度更快,什么时候会有明显的优化?这个问题我们之后也会讨论!)

但标准库的做法是:如果容器中类的移动操作函数带有noexcept声明,则使用移动操作来代替拷贝;如果没有noexcept声明,则用拷贝来完成扩容。

因此你会发现,将各种移动系列的函数设计为noexcept,会对标准库性能提升有巨大的帮助!

你可能感兴趣的:(C++学习笔记,c++,开发语言)