Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

学习自C++11异常处理 noexcept_木的情感的博客-CSDN博客_c++11 noexcept

C++03 异常处理(throw)

c++11也可以用

指定异常规格(语法)

C++98中,在函数声明时,我们使用throw指定一个函数可以抛出异常的类型(异常规格(exception specification))。例如:

class Ex {
public:
  double getVal();
  void display() throw();
  void setVal(int i) throw (char*, double);
 private:
   int m_val;
};12345678

上述函数的声明指定了该函数可以抛出异常的类型:
getVal() 可以抛出任何异常(默认);
display() 不可以抛出任何异常;
setVal() 只可以抛出char* 和 double类型异常。

编译器为了遵守C++语言标准,在编译时,只检查部分函数的异常规格(不会递归检查函数内部调用的所有函数抛出的异常类型是否符合异常规格)。

// declaration
extern void funAny(void);                   //May throw ANY exception.
void check(void) throw (std::out_of_range); // May throw only std::out_of_range.

// implementation
void check(void) throw(std::out_of_range) {
    funAny();   // Compiler does not check if funAny(), or one of its 
}               // subordinates, only throws std::out_of_range!

异常处理过程

使用throw指定异常规格的话, 如果函数抛出异常:

  • 异常处理机制会进行栈回退寻找(一个或多个)catch语句

  • 此时,检测catch可以捕捉的类型如果没有匹配的类型,**std::unexpected()**会被调用。

  • 但是std::unexpected()本身也可能抛出异常。如果std::unexpected()抛出的异常对于当前的异常规格是有效的,异常传递和栈回退会像以前那样继续进行

这意味着,如果使用throw, 编译器几乎没有机会做优化。事实上,编译器甚至会让代码变得更臃肿、庞大:
(1)栈必须被保存在回退表中;
(2)所有对象的析构函数必须被正确的调用(按照对象构建相反的顺序析构对象);
(3)编译器可能引入新的传播栅栏(propagation barriers)、引入新的异常表入口,使得异常处理的代码变得更庞大;
(4)内联函数的异常规格(exception specification)可能无效的。

C++11新增(noexcept)

语法

void mightThrow(); // could throw any exceptions.
void doesNotThrow() noexcept; // does not throw any exceptions

异常处理过程

当使用noexcept时,如果在该函数的上下文中抛出异常,std::teminate()函数会被立即调用(终止整个进程)(std::teminate()也可能抛出异常,其是noexcept),而不是调用std::unexpected();因此,在异常处理的过程中,编译器不会回退栈

比较noexcept和throw()

下面两个函数声明的异常规格在语义上是相同的,都表示函数不抛出任何异常。

void old_style() throw();
void new_style() noexcept;

但是throw()的会栈回退,noexcept的不会,这为编译器的优化提供了更大的空间。

建议

如果你知道你的函数绝对不会抛出任何异常,应该使用noexcept, 而不是throw().

评论