C++ 调用terminate时是否调用自动对象的析构函数?
当我们从析构函数中抛出C++ 调用terminate时是否调用自动对象的析构函数?,c++,stack,throw,brainbench,C++,Stack,Throw,Brainbench,当我们从析构函数中抛出时会发生什么?我知道它会导致调用terminate(),内存确实被释放,析构函数被调用,但是,这是在从foo调用throw之前还是之后?这里的问题可能是在堆栈展开时使用了throw,这就是问题所在。对象a是堆栈对象,因此没有要释放的动态内存。一旦控件超出了foo()的范围,堆栈框架和对象就不再存在了。为了说明这一点,下面是Microsoft C++中发生的情况: #include <iostream> class A { public: ~A() {
时会发生什么?我知道它会导致调用terminate()
,内存确实被释放,析构函数被调用,但是,这是在从foo
调用throw
之前还是之后?这里的问题可能是在堆栈展开时使用了throw
,这就是问题所在。对象a
是堆栈对象,因此没有要释放的动态内存。一旦控件超出了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;
}
如果我没有弄错的话,一旦调用了terminate
,就不会发生(进一步的)堆栈展开
terminate
调用处理程序函数(可以使用set\u terminate
设置该函数):
要执行的处理程序函数的类型
终止时由terminate()调用
异常处理。
必需
行为:
终止处理程序应终止程序的执行
不返回给呼叫方。
默认行为:
实现的默认terminate_处理程序调用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(){
puts(“输入析构函数”);
抛出“错误”;
}
};
void foo()
{
A,b;
抛出“错误”;
}
int main()
{
试一试{
foo();
}捕获(常量字符*){
返回1;
}
}
现在有两个A实例,第二个实例的析构函数永远不会被调用,因为第一个A的析构函数一完成,执行就被终止,并让另一个异常逃逸
这是在foo调用throw之前还是之后
这就是正在发生的事情:
- 调用
foo()
- 在堆栈上创建类型为
a
的对象a
- 下一个语句抛出
- 现在,调用
a
的dtor,这会引发另一个异常
- 调用了
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%
#包括
甲级{
公众:
~A()
{
fprintf(stderr,“in~A\n”);
抛出“错误”;
}
};
void foo()
{
A A;
fprintf(标准格式,“foo\n”);
抛出“错误”;
}
int main()
{
试一试{
foo();
}
捕获(常量字符*){
返回1;
}
返回0;
}
[~/ecc/ellcc/ecc]main%./a.out
因福
~A
在抛出“char const*”实例后调用terminate
中止
[~/ecc/ellcc/ecc]main%
正如您所看到的,抛出foo首先发生,然后抛出~A导致错误。您得到的结果稍有错误,这就是您不理解它的原因。您知道,抛出析构函数并不会导致调用teriminate()
函数,这是一种不好的做法,但对程序执行来说并不是致命的。致命的是,一些代码在仍然存在活动异常时抛出。C++不能决定进一步传播什么样的异常,新的或旧的异常,不能传播它们。它被认为对程序执行是致命的,这就是调用terminate的原因
因此,如果没有抛出foo
,将不会调用terminate,但会从~A
抛出异常。因此,很自然,必须首先调用throw-in-foo,然后在第二次throw过程中,所有内容都会中断。实际上,我相信您可以说,堆栈上的内存一旦超出范围,即使不是动态的,也会被释放。然而,这不是这里的问题,我也在问在析构函数中使用throw
。就像许多Brainbench问题一样,这属于“如果你做了一些你永远不应该做的事情会发生什么?”的范畴,因此不值得费心回答。@Neil,有时候这样的答案还是有用的。如果你知道如果你做了X
,就会发生S
,那么如果你在调试时看到S
,你就会看到你(或其他人)在某处做过的X
@Neil:是的,抛出一个const char*
。这太令人震惊了;-)我认为问题是好的,我宁愿有一个程序员知道为什么不从析构函数中抛出,因为他知道后果,而不是一个被他信任的权威命令不这样做,从不这样做,但不知道或不关心他为什么遵循这条规则的程序员。请注意,在这两者之间做出选择是一个好的开始。@Steve我的观点是,给定代码,标题问题并不是真正可以回答的。内存被释放了吗?是的,我想是这样的-如果调用了terminate,应用程序退出,所有内存都被释放,那又怎样?@nbolton:我想你可能要找的术语是“堆栈展开”。也许更好的标题是:“你是谁?”
#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%