考虑一下:
void thrower () {
throw "123";
}
struct Catcher {
~ Catcher () {
try {thrower ();}
catch (...) {}
}
};
int main () {
try {
Catcher c;
throw 1.23;
}
catch (...) {}
}
这在gcc 4.3上编译并运行时没有调用terminate
,但是according to the standard(15.5.1)
...当异常处理机制完成对要抛出的表达式的评估之后但在捕获异常之前(15.1),调用通过未捕获异常退出的用户函数...应该调用终止。< / p>
在抛出double之后调用~Catcher
时,这是“在完成评估之后......在捕获到异常之前”,并且thrower
是“通过未捕获的异常退出的用户函数” “,这符合上述条件。是的,char*
已被捕获,但只有在用户功能退出后。
不应该调用terminate
吗?
要强调这一点:
void do_throw () {
throw "123";
}
void thrower () {
do_throw ();
// Uncaught exception here (A)
}
struct Catcher {
~ Catcher () {
try {thrower (); /* (B) */}
catch (...) {}
}
};
int main () {
try {
Catcher c;
throw 1.23;
}
catch (...) {}
}
(A)发生在(B)的上下文中,已经有一个例外正在进行中。
那么,不应该调用terminate
吗?如果没有,这是一个法律情况,我们可以同时有两个例外,我们在哪里画线?
答案 0 :(得分:4)
为什么要调用terminate
?您有一个catch(...)
块可以捕获所有异常:一个捕获double
(main
中的一个),另一个捕获char const[4]
(~Catcher
中的一个}})。
因此没有函数“以未捕获的异常退出”,因为所有异常都被捕获。
这里的关键词是“调用以未捕获的异常终止的用户函数”。这与不相同“调用用户函数,该函数在线下某处调用以未捕获的异常终止的用户函数。”如果你调用一个函数并且它中有一个try/catch
块,然后你调用了一个函数,该函数以异常终止,但是你捕获异常,terminate
是不叫。
tl; dr:调用层次结构中的第一次调用,它来自异常评估之前和catch之前必须退出,并调用terminate
的异常, not 分支在第一个电话下方。这是有道理的,因为在评估被抛出的对象和被捕获的对象之间,你无法设置另一个 catch
块。
答案 1 :(得分:3)
由异常处理机制调用的函数通过一个退出
未捕获的异常。异常处理调用的唯一函数
这里的机制是Catcher::~Catcher()
,它正常退出
到达功能的最后。
从根本上说,同时数量没有限制
活跃的例外。限制是必须的例外数量
被传播到一个给定的点。如果Catcher::~Catcher()
退出
通过一个例外,我们将有两个例外来传播,一个
它触发了析构函数的调用,以及析构函数的调用
析构函数退出。只要析构函数捕获所有异常,
并且不传播它们,没有问题。
答案 2 :(得分:3)
...当异常处理机制完成评估后 要捕获的表达式,但在捕获异常之前 (15.1),调用通过未捕获的异常退出的用户函数... 终止应该被召唤。
异常处理机制调用~Catcher()
,它不会通过未捕获的异常退出;因此它不会调用std::terminate
。它没有(直接)调用thrower()
。
答案 3 :(得分:3)
不同的重点:
当异常处理机制时,在完成对要抛出的表达式的评估之后但在异常被捕获之前(15.1),调用通过一个退出的用户函数未被捕获的例外...终止应被称为
thrower()
是一个通过异常退出的用户函数,但异常处理机制不会调用它。它由另一个用户函数(Catcher
的析构函数)调用,该函数本身由异常处理机制调用,此函数不会通过异常退出。
答案 4 :(得分:0)
这是完全合法的代码。你抓住了它引发问题之前抛出的所有东西。 只有当您删除~Matcher中的try / catch时,您才会在展开期间抛出异常。