深入使用noexcept

简介

noexcept是C++11引入的,表明函数是否会抛出异常。正确使用它可以优化性能,错误使用则会带来麻烦。

noexcept使用语法有两种:

  1. noexcpet
  2. noexcept(expression)

第二种使用方式允许用表达式决定是否noexcept起效果,当expression的值为true的时候起效果,否则不起效。expression是编译时求值,一切都是在编译时决定。

好处

如果一个函数标注成noexcept

  • 可以选择移动构造函数 / 移动赋值运算符;
  • 编译器就不用生成异常处理代码了,因此可以优化编译。

坏处

如果noexcept的函数执行时出了异常,包括所调用的函数抛出的异常,程序会马上terminate,即使套上try...catch也仍旧会terminate。并且编译器不会帮你检查这样的风险。

适用场景

在需要决定是调用移动构造函数(或者移动赋值运算符)还是复制构造函数(或者复制赋值运算符)时,noexcept会影响决定。因为移动语法会“破坏”原来的源对象的内容,造成无法在出现异常情况下恢复状态。因此只有移动构造函数(或者移动赋值运算符)标明为noexcept时,才能在这种情况下使用移动构造函数(或者移动赋值运算符)替代复制构造函数(或者复制赋值运算符)。

一个例子就是STL库中的vector的扩容,扩容涉及到是复制对象还是移动对象的问题,就是上述的问题。当对象的移动构造函数可能会抛出异常的时候,vector是”不敢“在这个场景下调用的,因为出了异常无法原恢复状态。

详细逻辑如下:

  1. 当使用复制构造时,抛出异常时只要把已复制的对象销毁,新分配的内存释放,一切还能恢复到跟以前一样;
  2. 当使用移动构造时,容器中的原来的元素的状态已经被移动构造破坏了,无法恢复到跟以前一样。

以下是测验代码:

class A {
public:
    A() { std::cout << "constructor" << std::endl; }
    A(const A& a) { std::cout << "copy constructor" << std::endl; }
    A(const A&& a) noexcept { std::cout << "move constructor" << std::endl; } // 有noconcept时,扩容时用移动构造
    // A(const A&& a) { std::cout << "move constructor" << std::endl; }  // 去掉noconcept时,扩容时用拷贝构造
};

int main() {
    std::vector<A> v;
    v.reserve(1);
    for (int i = 0; i < 10; i++) {
        A a; // 构造一个A类的实例。
        v.push_back(a); // 添加进容器时会调用一次复制构造,如果容量不够则会扩容,这时候会选择复制构造还是移动构造。
    }

    return 0;
}

不适用场景

其他情况均不太适合使用。因为:

  1. 编译器不会帮你做检查,假如一个标注noexcept的函数调用未标注noexcept的函数,是可以顺利编译的。但是未标注nonexcept的函数是不保证不抛异常的。
  2. 一个函数加上noexcept之后就可能很难移除,因为其他代码可能会直接或间接引用到它,且假设不会有异常;
  3. 如果一个标注了noexcept的函数自己或者调用的函数(直接或间接)抛出异常,直接终止,非常简单粗暴。

因此出了几个有限的适用场景外,其他情况下不要用noexcept

实验结果

以下图表是来自于 C++ noexcept and move constructors effect on performance in STL Containers — TRYING TO FIND THE OBVIOUS (hlsl.co.uk) 这篇博客的实验结果。
在这里插入图片描述
根据实验结果,性能提升了两倍!

总结

虽然noexcept会在某些情况下提升性能,但是由于它的危险性,包括发生异常直接终止程序且编译器不会帮你检查,除了以下情况下都不建议使用。

  1. 移动构造函数
  2. 移动赋值运算符
  3. 析构函数
  4. 简单函数

1、2 已经在前面说过了,不再赘述。对于3、4,析构函数本身不应该抛异常,简单函数一般不会发生异常,因此可以放心标注noexcept

参考资料

  1. c++ 从vector扩容看noexcept应用场景 - 知乎 (zhihu.com)

  2. C++ noexcept and move constructors effect on performance in STL Containers — TRYING TO FIND THE OBVIOUS (hlsl.co.uk)

  • 12
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值