当我们throw
使用析构函数时会发生什么?我知道它会导致terminate()
被调用,并且确实释放了内存并调用了析构函数,但是,这是在调用之前还是之后 ?也许这里的问题是在堆栈展开时使用它是问题所在。throw
foo
throw
6 回答
这是从 foo 调用 throw 之前还是之后?
这是正在发生的事情:
foo()
叫做- 在栈上创建一个
a
类型的对象A
- 下一条语句抛出
- 现在,调用了 dtor for
a
,这引发了另一个异常 std::terminate
被调用——这只不过是放弃了异常处理机制:
来自 C++0x 草案:
15.5.1 std::terminate() 函数
1在以下情况下,必须放弃异常处理以使用不太微妙的错误处理技术:
[...] — 当堆栈展开 (15.2) 期间对象的破坏使用异常退出时,或
2在这种情况下,调用 std::terminate() (18.7.3)。在没有找到匹配处理程序的情况下,在调用 std::terminate() 之前堆栈是否展开是实现定义的。在所有其他情况下,堆栈不应在 std::terminate() 被调用之前展开。基于展开过程最终将导致调用 std::terminate() 的确定,不允许实现提前完成堆栈展开。
注意:强调我的
以下是 g++ 中发生的情况:
#include <stdio.h>
class A {
public:
~A()
{
fprintf(stderr, "in ~A\n");
throw "error";
}
};
void foo()
{
A a;
fprintf(stderr, "in foo\n");
throw "error";
}
int main()
{
try {
foo();
}
catch (const char*) {
return 1;
}
return 0;
}
[~/ecc/ellcc/ecc] main% ./a.out
in foo
in ~A
terminate called after throwing an instance of 'char const*'
Abort
[~/ecc/ellcc/ecc] main%
如您所见, foo 中的抛出首先发生,然后 ~A 中的抛出导致错误。
如果我没记错的话,一旦terminate
被调用,就不会发生(进一步)堆栈展开。
terminate
调用处理函数(您可以使用 设置set_terminate
):
终止异常处理时由 terminate() 调用的处理程序函数的类型。
必需的行为:terminate_handler 应终止程序的执行而不返回调用者。
默认行为: 实现的默认 terminate_handler 调用 abort()。
至少我不知道一种“终止执行而不返回调用者”的方法,它可以让你展开堆栈。
您可以修改示例以查看您的预期:
#include <cstdio>
class A
{
public:
~A() {
puts("Entered A destructor");
throw "error";
}
};
void foo()
{
A a, b;
throw "error";
}
int main()
{
try {
foo();
} catch (const char*) {
return 1;
}
}
现在有两个 A 实例,第二个的析构函数永远不会被调用,因为一旦第一个 A 的析构函数完成并让另一个异常逃逸,执行就终止了。
你得到的有点错误,这就是你不理解它的原因。你看,在析构函数中抛出并不会导致teriminate()
函数被调用,这是一种不好的做法,但对于程序执行来说并不是致命的。致命的是一些代码在仍然存在活动异常时抛出。C++ 不能决定进一步传播什么异常,新的还是旧的,它不能同时传播它们。它被认为对程序执行是致命的,这就是调用 terminate 的原因。
所以,你看,没有 throw in foo
, terminate 不会被调用,但是会有一个异常 from 抛出~A
。因此,很自然地,必须首先调用 throw in foo,然后在第二次 throw 期间,一切都会中断。
Objecta
是一个栈对象,所以没有动态内存要释放。一旦控制超出了 的范围foo()
,堆栈帧和对象就不再存在。
为了说明,以下是 Microsoft C++ 中发生的情况:
#include <iostream>
class A {
public:
~A() {
std::cout << "in ~A" << std::endl;
throw "error";
}
};
void foo() {
A a;
std::cout << "in foo" << std::endl;
throw "error";
}
int main() {
try {
foo();
}
catch (void *) {
std::cout << "exit: 1" << std::endl;
return 1;
}
std::cout << "exit: 0" << std::endl;
return 0;
}
结果:
>cpptest1.exe
in foo
in ~A
This application has requested the Runtime to terminate it in an unusual way.
Please contact the application's support team for more information.
>