看这篇文章学习C++异常处理的基础知识。看完后,还不过瘾,为什么大家在C++代码中都不用Exception?为什么C++11会引入一些变化?
为什么C++ exception handling需要unwind stack?
为什么throw会被抛弃?
接着看http://www.gotw.ca/publications/mill22.htm, 总结如下:
throw() specification 可以让程序员在自己写的函数上标注该函数是否会抛出异常, 会抛出哪些类型的异常,但是throw有如下的问题:
1. C++基于throw的异常处理部分是一个“Shadow Type System”
为什么这么说呢?throw()语法有时被认为是函数签名的一部分,而有的时候却不允许,而这没有标准规定,比如下面的例子
1
2
3
4
5
6
7
8
|
// Example 2(a): You can’t write an ES // in a typedef. // void
f() throw (A,B);
typedef
void (*PF)() throw (A,B); // syntax error
PF pf = f; // can’t get here
|
在typdef时不把throw作为函数类型的一部分,但看下面的例子:
1
2
3
4
5
6
|
// Example 2(b): But you can if you omit // the typedef! // void
f() throw (A,B);
void
(*pf)() throw (A,B); // ok
pf = f; // ok
|
还有函数指针的例子:
1
2
3
4
5
6
|
// Example 2(c): Also kosher, low-carb, // and fat-free. // void
f() throw (A,B);
void
(*pf)() throw (A,B,C); // ok
pf = f; // ok, less restrictive
|
还有继承的virtual 函数的例子:
1
2
3
4
5
6
7
8
9
10
11
12
|
// Example 2(d): And the ES in the signature // does matter if it’s a virtual function. // class
C
{ virtual
void f() throw (A,B); // same ES
}; class
D : C
{ void
f(); // error, now the ES matters
}; |
2. 对throw语法的(错误)理解
很多人认为throw表示下面的意思:
1. Guarantee that functions will only throw listed exceptions
(possibly none).
2. Enable compiler optimizations based on the
knowledge that only listed exceptions (possibly none) will be
thrown.
对第一条,看下面的代码:
1
2
3
4
5
6
|
// Example 1(b) reprise, and two // potential white lies: // int
Gunc() throw (); // will throw nothing (?)
int
Hunc() throw (A,B); // can only throw A or B (?)
|
Gunc()真的不会throw任何异常吗?Hunc()真的只会抛出类型A和B的异常吗?不是的,代码复杂、调用嵌套多次后,很多时候程序员是无法准确标注这个函数会throw什么样的exception。而一旦未被标注的异常发生后,编译器也只是默默地做点事情,而这对我们的程序没有什么帮助。如果一个未被标准的异常发生后,编译器就调用std::unexpected()函数。unexpected()函数是全局的,很难对特定的exception提供很有帮助的处理,大部分情况就直接terminate,而且unexpected()函数是不会返回的,所以,这样的异常一旦发生,就等于退出程序。
对throw的理解应该换成下面的两句:
- Guarantee Enforce at runtime that functions will only throw listed exceptions (possibly none).
- Enable or prevent compiler optimizations based on the knowledge that only listed exceptions (possibly none) will be thrown having to check whether listed exceptions are indeed being thrown.
下面看看编译器干了什么,对下面的代码:
1
2
3
4
5
6
|
// Example 3(a) // int
Hunc() throw (A,B)
{ return
Junc();
} |
编译器生成如下代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
// Example 3(b): A compiler’s massaged // version of Example 3(a) // int
Hunc()
try { return
Junc();
} catch ( A )
{ throw ;
} catch ( B )
{ throw ;
} catch ( ... )
{ std::unexpected(); // will not return! but
} // might throw an A or a B if you’re lucky
|
可见,编译器并不是根据listed exception做优化,编译器需要生成更多的代码来保证在运行时只有listed exception被throw出来,而没有list就调用unexpected函数了。
在回头看对throw的正确的理解:
1. 保证运行时,只会throw listed exception,而如果发生不listed 的exception,那就调用unexpected;
2. 允许或者禁止编译器不得不进行是否listed exception发生的检查。
既然throw有如此多的问题,那C++11带来了什么呢?
继续看
Reference:
1. http://www.learncpp.com/cpp-tutorial/153-exceptions-functions-and-stack-unwinding/
2. http://www.gotw.ca/publications/mill22.htm
3. http://*.com/questions/88573/should-i-use-an-exception-specifier-in-c/88905#88905
4. http://*.com/questions/10787766/when-should-i-really-use-noexcept