189 8069 5689

深入使用noexcept-创新互联

深入使用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 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)

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


分享题目:深入使用noexcept-创新互联
当前URL:http://jkwzsj.com/article/icojd.html

其他资讯