详情
C++相比C的一大优势在于C++本身就定义了一套完整的异常优化处理机制。比如:void excpt_func() throw(int, double){}
- 在C++11之前,几乎没有人去使用在函数名后书写异常声明表达式
- 从C++11开始,这套机制被弃用,我们不应该再去了解使用它。
C++11将异常的声明简化为下面两种情况:
- 函数可能抛出异常
- 函数不可能抛出异常
并且用noexcept对这两种行为进行限制,比如:
void may_throw(); // 可能抛出异常
void no_throw() noexcept; //不可能抛出异常
noexcept
:表明起修饰的函数不会抛出异常- 使用
noexcept
修饰过的函数如果抛出异常,编译器会直接使用std::terminate()
来立即终止程序
语法上,noexcept修饰符有两种形式,
- 一种是简单的在函数声明后加上noexcept关键字,比如:
void excpt_func() noexcept;
- 另一种是可以接受一个常量表达式作为参数,常量表达式的结果会被转换为一个bool类型的值,为true时表示函数不会抛出异常,否则则有可能抛出异常。比如:
void excpt_func() noexcept(常量表达式);
noexcept修饰完一个函数之后能够起到封锁异常扩散的功效,如果内部产生异常,外部也不会触发(当catch到异常,编译器会直接调用std::terminate终端程序的执行,从而阻止异常的继续传播),可以有效提供程序的性能
try {
may_throw();
}catch(...) {
printf("捕获异常,来自my_throw\n");
}
try {
non_block_throw();
}catch(...) {
printf("捕获异常,来自non_block_throw\n");
}
try {
blcok_throw();
}catch(...) {
printf("捕获异常,来自blcok_throw\n");
}
nexcept
还能够做操作符,用于操作一个表达式,当表达式无异常时,返回true,否则返回false
#include <iostream>
void may_throw(){
throw true;
}
auto non_block_throw = []{
may_throw();
};
void no_throw() noexcept {
return;
}
auto blcok_throw = []() noexcept{
no_throw();
};
int main() {
std::cout << std::boolalpha
<< " may_throw() noexcept ?" << noexcept(may_throw()) << "\n"
<< " no_throw() noexcept ?" << noexcept(no_throw()) << "\n"
<< " lmay_throw() noexcept ?" << noexcept(non_block_throw()) << "\n"
<< " lno_throw() noexcept ?" << noexcept(blcok_throw()) << "\n";
return 0;
}
nexcept
做操作符时,通常用于模板:
template<class T>
void func() noexcept(noexcept(T())){}
这里,func函数是否时一个noexcept函数,将由T()表达式是否会抛出异常所决定。如果参数是一个可能抛出异常的就返回false,否则返回true。
nexcept
更大的作用是保证应用程序的安全,比如一个类析构函数不应该抛出异常,那么对于常被析构函数调用的delete函数来说,C++11默认将delete函数设置成noexcept,就可以提高程序的安全型:
void operator delete(void *) noexcept;
void operator delete[](void *) noexcept;
而同样处于安全考虑,C++11标准中让类的析构函数默认也是noexcept(true)的。当然,如果程序员显式的为析构函数指定了noexcept,或者类的基类或者成员有noexcept(true)的析构函数,析构函数就不会再保持默认值,看个例子:
#include <iostream>
using namespace std;
struct A{
~A() {throw 1;}
};
struct B{
~B() noexcept(false){throw 2;}
};
struct C{
B b;
};
int funcA(){
A a;
}
int funcB(){
B a;
}
int funcC(){
C a;
}
int main(){
try {
funcB(); //
}catch (...){
printf("caught funcB\n");
}
try {
funcA(); // terminate called after throwing an instance of 'int'
}catch (...){
printf("caught funcA\n");
}
try {
funcC(); //
}catch (...){
printf("caught funcB\n");
}
}
上面,除了类A(析构函数被默认为noexcept(false))可以阻止异常扩散外,B和C的析构函数都可以抛出异常