1. 关键字noexcept
从C++11开始,我们能看到很多代码当中都有关键字noexcept
。比如下面就是std::initializer_list
的默认构造函数,其中使用了noexcept
。
constexpr initializer_list() noexcept
: _M_array(0), _M_len(0) { }
该关键字告诉编译器,函数中不会发生异常,这有利于编译器对程序做更多的优化。
如果在运行时,noexecpt
函数向外抛出了异常(如果函数内部捕捉了异常并完成处理,这种情况不算抛出异常),程序会直接终止,调用std::terminate()
函数,该函数内部会调用std::abort()
终止程序。
2. C++的异常处理
C++中的异常处理是在运行时而不是编译时检测的。为了实现运行时检测,编译器创建额外的代码,然而这会妨碍程序优化。
在实践中,一般两种异常抛出方式是常用的:
- 一个操作或者函数可能会抛出一个异常;
- 一个操作或者函数不可能抛出任何异常。
后面这一种方式中在以往的C++版本中常用throw()
表示,在C++ 11中已经被noexcept
代替。
void swap(Type& x, Type& y) throw() //C++11之前
{
x.swap(y);
}
void swap(Type& x, Type& y) noexcept //C++11
{
x.swap(y);
}
3. noexcep的使用
- 当
noexcept
是标识符时, 它的作用是在函数后面声明一个函数是否会抛出异常. - 当
noexcept
是函数时, 它的作用是检查一个函数是否会抛出异常.
备注:
noexcept
编译期完成声明和检查工作.
noexcept
主要是解决的问题是减少运行时开销. 运行时开销指的是, 编译器需要为代码生成一些额外的代码用来包裹原始代码,当出现异常时可以抛出一些相关的堆栈stack unwinding
错误信息, 这里面包含,错误位置, 错误原因, 调用顺序和层级路径等信息.
当使用noexcept
声明一个函数不会抛出异常候, 编译器就不会去生成这些额外的代码, 直接的减小的生成文件的大小, 间接的优化了程序运行效率.
4. noexcept作为标识符
noexcept
标识符有几种写法: noexcept
、noexcept(true)
、noexcept(false)
、noexcept(expression)
、throw()
.
其中noexcept
默认表示noexcept(true)
.
当noexcept
是true
时表示函数不会抛出异常,
当noexcept
是false
时表示函数可能会抛出异常.
throw()
表示函数可能会抛出异常, 不建议使用该写法, 应该使用noexcept(false)
, 因为 C++20 放弃这种写法.
// noexcept 标识符
// noexcept 相当于 noexcept(true)
// 声明 noexcept(true)之后, 将表示这个是不会报错的.
// 如果报错的话, 进程直接结束, 不会抛出异常信息.
void example() noexcept {
cout << "hello called" << endl;
}
5. noexcept作为函数
noexcept
函数用来检查一个函数是否声明了noexcept
, 如果声明了noexcept(true)
则返回true, 如果声明了noexcept(false)
则返回false.
#include <iostream>
using std::cout;
using std::endl;
using std::boolalpha;
// noexcept 标识符
void foo() noexcept(true) {
throw 4;
}
// noexcept 标识符
void bar() noexcept(false) {
throw 4;
}
int main(void) {
// noexcept 函数
cout << boolalpha << noexcept(foo()) << endl; // true
cout << boolalpha << noexcept(bar()) << endl; // false
return 0;
}
noexcept
函数 还可以在常规函数中配合noexcept(expression)
标识符 共同完成对其他函数是否声明了 noexcept
的检查.
#include <iostream>
using std::cout;
using std::endl;
using std::boolalpha;
struct foo {
int a;
void getFoo() noexcept(true) {
cout << "foo.getFoo called" << endl;
}
void getBar() noexcept(false) {
cout << "foo.getBar called" << endl;
}
};
template<typename T>
void example_true(T t) noexcept(noexcept(t.getFoo())) {
cout << "example called" << endl;
}
template<typename T>
void example_false(T t) noexcept(noexcept(t.getBar())) {
cout << "example called" << endl;
}
int main(void) {
foo x{};
cout << boolalpha << noexcept(example_true(x)) << endl; // true
cout << boolalpha << noexcept(example_false(x)) << endl; // false
return 0;
}