noexcept
是C++11引入的,表明函数是否会抛出异常。正确使用它可以优化性能,错误使用则会带来麻烦。
noexcept
使用语法有两种:
第二种使用方式允许用表达式决定是否noexcept
起效果,当expression
的值为true的时候起效果,否则不起效。expression
是编译时求值,一切都是在编译时决定。
如果一个函数标注成noexcept
,
如果noexcept的函数执行时出了异常,包括所调用的函数抛出的异常,程序会马上terminate,即使套上try...catch
也仍旧会terminate。并且编译器不会帮你检查这样的风险。
在需要决定是调用移动构造函数(或者移动赋值运算符)还是复制构造函数(或者复制赋值运算符)时,noexcept
会影响决定。因为移动语法会“破坏”原来的源对象的内容,造成无法在出现异常情况下恢复状态。因此只有移动构造函数(或者移动赋值运算符)标明为noexcept
时,才能在这种情况下使用移动构造函数(或者移动赋值运算符)替代复制构造函数(或者复制赋值运算符)。
一个例子就是STL库中的vector
的扩容,扩容涉及到是复制对象还是移动对象的问题,就是上述的问题。当对象的移动构造函数可能会抛出异常的时候,vector
是”不敢“在这个场景下调用的,因为出了异常无法原恢复状态。
详细逻辑如下:
以下是测验代码:
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;
}
不适用场景其他情况均不太适合使用。因为:
noexcept
之后就可能很难移除,因为其他代码可能会直接或间接引用到它,且假设不会有异常;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,析构函数本身不应该抛异常,简单函数一般不会发生异常,因此可以放心标注noexcept
。
c++ 从vector扩容看noexcept应用场景 - 知乎 (zhihu.com)
C++ noexcept and move constructors effect on performance in STL Containers — TRYING TO FIND THE OBVIOUS (hlsl.co.uk)
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
售后响应及时
7×24小时客服热线数据备份
更安全、更高效、更稳定价格公道精准
项目经理精准报价不弄虚作假合作无风险
重合同讲信誉,无效全额退款