是否在调用终止时调用自动对象的析构函数?

时间:2010-03-13 12:38:52

标签: c++ stack throw brainbench

当我们从析构函数中throw时会发生什么?我知道它会导致terminate()被调用,内存确实被释放并且析构函数被调用,但是, throw之前或之后的是从{{1 }}?也许这里的问题是在堆栈放松时使用foo是问题所在。

6 个答案:

答案 0 :(得分:13)

  

是从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 ::终止()。

     

注意:强调我的

答案 1 :(得分:2)

以下是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会导致错误。

答案 2 :(得分:1)

如果我没有弄错的话,一旦调用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的析构函数完成并且让另一个异常转义就会终止执行。

答案 3 :(得分:1)

你有点错误,这就是你不明白的原因。你看,抛出析构函数不会导致teriminate()函数被调用,这是一个不好的做法,但它对程序执行并不致命。什么是致命的是一些代码在仍有活动异常的情况下抛出。 C ++无法决定进一步传播哪个异常,新的或旧的异常,并且它们都不能传播它们。程序执行被认为是致命的,这就是调用终止的原因。

所以,你看,如果没有抛出foo,将不会调用terminate,但会从~A抛出异常。所以,当然,必须首先调用foo,然后在第二次投掷期间,所有内容都会中断。

答案 4 :(得分:0)

对象a是一个堆栈对象,因此没有动态内存可供释放。一旦控制超出foo()的范围,堆栈框架以及对象就不再存在。

答案 5 :(得分:0)

为了说明,这是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.

>