C++ - 捕获双重异常

3

I have the following code:

#include <iostream>
using namespace std;

class A {
public:
    A()  { cout << "A::A()" << endl;}
    ~A() { cout << "A::~A()" << endl; throw "A::exception";}
};

class B {
public:
    B()  { cout << "B::B()" << endl; throw "B::exception";}
    ~B() { cout << "B::~B()";}
};

int main() {
    try {
        cout << "Entering try...catch block" << endl;
        A   objectA;
        B   objectB;
        cout << "Exiting try...catch block" << endl;
    } catch (char const * ex) {
        cout << ex << endl;
    }
    return 0;
}

在提出问题之前,我想指出这段代码是不好的实践(例如从构造函数抛出异常会导致对象没有完全创建,因此析构函数不会被调用,可能会导致内存泄漏或其他问题)。

现在,主要过程的顺序如下:

  1. 打印 "进入 try...catch 块"

  2. 调用 A 的构造函数,打印 "A::A()"

  3. 调用 B 的构造函数,打印 "B::B()",并抛出异常。

  4. 抛出异常,行 "退出 try...catch 块" 将不会被打印。块已退出,因此调用 A 的析构函数。

  5. A 的析构函数打印 "A::~A()" 并抛出另一个异常。

第二个异常(在5中)会导致主程序抛出异常,然后进入 catch 块。

我的问题是 - 是否有办法在不更改类 AB 的情况下,在主程序中捕获第二个异常?

我尝试在整个 try-catch 块和 catch 块内部周围包裹另一个 try-catch 块,但没有奏效。

谢谢。


7
在堆栈展开期间在析构函数中抛出异常会导致程序终止,这是一个非常不好的习惯,在析构函数中抛出异常应该尽量避免。 - Tyker
任何时候只能有一个活动异常。 - Yksisarvinen
3
小注释:在构造函数中抛出异常通常是完全可以的。 - Mike Vine
3
顺便说一句,从构造函数中抛出异常并不是坏事,这几乎是在构建过程中发生错误时的唯一方式。但如果您使用原始指针,则应该注意它们。 - Yksisarvinen
1
只是一个练习,你可以使用内部的try {B objectB;} catch(...){}并单独捕获该异常。虽然有一定用处。 - Bo Persson
2个回答

5

来自cppreference.com

与任何其他函数一样,析构函数可能通过抛出异常而终止执行[...] 但是如果此析构函数在堆栈展开期间被调用,则会调用std::terminate函数。

因此,从~A()中尝试抛出异常并不会导致第二个异常被抛出;它会导致程序终止。如果您需要“捕获”这个“第二个异常”,则需要干预终止处理程序。或者您可以找到一种方法,在析构函数中避免抛出异常。接下来引用自cppreference.com:

虽然有时可以使用std::uncaught_exception来检测正在进行的堆栈展开,但通常认为允许任何析构函数通过抛出异常而终止执行是不好的实践。


0
你可以使用set_terminate + longjmp来避免程序终止。
#include <iostream>
#include <setjmp.h>


using namespace std;

jmp_buf jmpBuf;

class A {
public:
    A()  { cout << "A::A()" << endl;}
    ~A() noexcept(false){ cout << "A::~A()" << endl; throw "A::exception";}
};

class B {
public:
    B()  { cout << "B::B()" << endl; throw "B::exception";}
    ~B() { cout << "B::~B()";}
};

int main() {

    set_terminate([](){
        cout<<"long jump begin" << endl;
        longjmp(jmpBuf,1);
    });

    if(setjmp(jmpBuf)==0) {

        try {
            cout << "Entering try...catch block" << endl;
            A objectA;
            B objectB;
            cout << "Exiting try...catch block" << endl;
        } catch (char const *ex) {
            cout << ex << endl;
        }
    }else{
        cout<<"long jump end" << endl;
    }
    return 0;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接