文中的例子使用 g++ 4.5.2 和 clang++ 2.9 编译测试通过.
如果一个函数声明了异常声明与该函数实际抛出的异常不匹配, 那么这个异常不会被捕获到. 也就是说
catch (...)
不是银弹.#include <stdexcept>
void func_throws() throw (std::out_of_range)
{
throw std::logic_error("danger!");
}
int main()
{
try {
func_throws();
}
catch (std::out_of_range) {}
catch (std::logic_error) {}
catch (...) {}
return 0;
}
std::set_unexpected
函数与不预期的异常而一个有趣的 C++ 规定是, 如果一个函数没有任何异常声明, 则该函数有可能抛出任何异常. 这个规定的一个隐喻是, 一切 C 函数都有可能抛出任何异常. 虽然 C 连异常是什么都不知道, 不过没关系. 因为 C 函数是异常透明的.
比如下面的例子
#include <stdexcept>
extern "C" void exception_pass();
void func_throws()
{
throw std::logic_error("danger!");
}
void upper_cpp_func()
{
exception_pass();
}
int main()
{
try {
upper_cpp_func();
} catch (std::logic_error) {
return 0;
}
return 1;
}
extern "C" void exception_pass()
{
func_throws();
}
另外, 分离编译再链接时行为会不同, 可能出现异常无法被捕获的情况.
/* a.cpp */
#include <stdexcept>
extern "C" void exception_pass();
extern "C" void upper_c_func();
void func_throws()
{
throw std::logic_error("danger!");
}
void upper_cpp_func()
{
upper_c_func();
}
int main()
{
try {
upper_cpp_func();
} catch (std::logic_error) {
return 0;
}
return 1;
}
extern "C" void exception_pass()
{
func_throws();
}
/* b.c */
void exception_pass();
void upper_c_func()
{
exception_pass();
}
/* to compile */
g++ a.cpp -c
gcc b.c -c
g++ a.o b.o -o a.out
/* or */
clang++ a.cpp -c
clang b.c -c
clang++ a.o b.o -o a.out
Python 或 Java 这样的语言能够在虚拟机一层检测低级错误 (这里的 "低级" 指的是靠近底层, 不是说程序员脑子进水) 并向上报告异常. 如果把操作系统想象成 C++ 的虚拟机, 实际上很多错误也能变成异常, 只是没有 stacktrace 而已.
甚至一些 CPU 指令级别的错误, 得益于 POSIX signal 机制, 都能高效的被直接包装成 C++ 异常. 请参考 C++ 中捕获整数除零错误. 由于 signal 的发生相当于在当前调用栈上直接垒上另一个栈帧, 所以没问题, 果断抛吧.