c++ 可否同时抛出两个异常

在 C++ 中,如果我们抛出异常后 ,在捕获异常之前,会析构掉所有还在堆栈上的对象。

#include <iostream>
using namespace std;
class Object {
  public:
   ~Object();
};
Object::~Object() { cout << "Deconstructor is invoked." << endl; }
void foo() {
    try {
        Object obj;
        throw invalid_argument("only for testing");
    }catch(const exception & e) {
        cout << "catch exception: " << e.what() << endl;
    }
}
int main(int argc, char *argv[])
{
    foo();
    return 0;
}

这个时候,程序的输出是

Deconstructor is invoked.
catch exception: only for testing

这里只有一层函数调用,如果有多层函数调用,也是类似的。

#include <iostream>
using namespace std;
class Object {
  public:
   Object(int value) : value_(value){};
   ~Object();
   int value_;
};
Object::~Object() {
  cout << "Deconstructor is invoked. value=" << value_ << endl;
}
void foo3() { throw invalid_argument("only for testing"); }
void foo2() {
    auto object = Object(2);
    foo3();
}
void foo1() {
    auto object = Object(1);
    foo2();
}
int main(int argc, char *argv[])
{
    try {
        foo1();
    }catch(const exception & e) {
        cout << "catch exception: " << e.what() << endl;
    }
    return 0;
}

程序输出结果是

Deconstructor is invoked. value=2
Deconstructor is invoked. value=1
catch exception: only for testing

可以看到,堆栈上的所有对象都被析构掉了。调用析构函数的顺序和调用构造函数的顺序相反。也就是说,堆栈上的对象按照被构造的顺序,反序析构。

什么叫做“同时抛出两个异常” ? 在上面的例子中,如果我们在析构函数中再抛出一个异常,这样在捕获异常之前,就会同时存在两个异常。

#include <iostream>
using namespace std;
class Object {
  public:
   ~Object();
};
Object::~Object() {
  cout << "Deconstructor is invoked." << endl;
  throw invalid_argument("another exception");
}
void foo() {
    try {
        Object obj;
        throw invalid_argument("only for testing");
    }catch(const exception & e) {
        cout << "catch exception: " << e.what() << endl;
    }
}
int main(int argc, char *argv[])
{
    foo();
    return 0;
}

在 C++ 中,如果像这样同时存在两个异常,那么程序会调用 std::terminate ,程序异常退出。

上面的例子中,输出结果是

Deconstructor is invoked.
libc++abi.dylib: terminating with uncaught exception of type std::invalid_argument: another exception

于是,不要在析构函数里面抛出异常