2

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

4

6 回答 6

13

这是从 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() 的确定,不允许实现提前完成堆栈展开。

注意:强调我的

于 2010-03-13T12:55:25.203 回答
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 中的抛出导致错误。

于 2010-03-13T12:55:32.533 回答
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 的析构函数完成并让另一个异常逃逸,执行就终止了。

于 2010-03-13T12:52:35.677 回答
1

你得到的有点错误,这就是你不理解它的原因。你看,在析构函数中抛出并不会导致teriminate()函数被调用,这是一种不好的做法,但对于程序执行来说并不是致命的。致命的是一些代码在仍然存在活动异常时抛出。C++ 不能决定进一步传播什么异常,新的还是旧的,它不能同时传播它们。它被认为对程序执行是致命的,这就是调用 terminate 的原因。

所以,你看,没有 throw in foo, terminate 不会被调用,但是会有一个异常 from 抛出~A。因此,很自然地,必须首先调用 throw in foo,然后在第二次 throw 期间,一切都会中断。

于 2010-03-13T13:22:57.927 回答
0

Objecta是一个栈对象,所以没有动态内存要释放。一旦控制超出了 的范围foo(),堆栈帧和对象就不再存在。

于 2010-03-13T12:43:55.530 回答
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.

>
于 2010-03-13T12:46:01.890 回答