C++ 中的异常处理程序 catch(...)

Exception handler catch(...) in C++

我知道 C++ 中的异常处理程序 catch(...) 可以处理任何类型的异常。我想知道如果它必须处理某种 class 类型的异常会发生什么 - class 的对象是通过引用还是通过值传递给此处理程序?

这是有助于理解这一点的代码。当“Inner try”抛出类型B的异常时,用classB的默认构造函数创建了一个classB的未命名临时对象。当我运行这个程序时,复制class B 的构造函数根本没有被调用,class B 的这个未命名的临时对象仅在执行“Outer try 3”后才被销毁,这意味着该对象一直向后传播到“外部尝试3”。所以我的猜测是对象通过引用传递给 catch(...) 处理程序,但我想看看这是否是正确的推理。

    #include <iostream>
    
    using namespace std;
    
    class A {
    public:
        A() {
            cout << "Default constructor of class A" << endl;
        }
        A(const A& a) {
            cout << "Copy constructor of class A" << endl;
        }
        A(A&& a) noexcept {
            cout << "Move constructor of class A" << endl;
        }
        ~A() {
            cout << "Destructor of class A" << endl;
        }
    };
    
    class B {
    public:
        B() {
            cout << "Default constructor of class B" << endl;
        }
        B(const B& b) {
            cout << "Copy constructor of class B" << endl;
        }
        B(B&& b) noexcept {
            cout << "Move constructor of class B" << endl;
        }
        ~B() {
            cout << "Destructor of class B" << endl;
        }
    };
    
    int main() {
        try {
            try {
                try {
                    try {
                        throw A();
                    }
                    catch (A a) {
                        cout << "Inner try" << endl;
                        throw B();
                    }
                }
                catch (...) {
                    cout << "Outer try 1" << endl;
                    throw;
                }
            }
            catch (...) {
                cout << "Outer try 2" << endl;
                throw;
            }
        }
        catch (...) {
            cout << "Outer try 3" << endl;
        }
    }

当你抛出一个异常时,异常对象从操作数到throw被创建。它是以未指定方式存储的对象。

当到达异常的匹配处理程序并且处理程序不是 ... 时,处理程序的参数将从异常对象初始化。

本质上,如果您将 catch 参数声明为 non-reference 类型,您将在处理程序中获得异常对象(或基础 class 子对象)的副本.这就是您在 A 处理程序中看到的内容以及调用复制构造函数的原因。副本一直存在,直到处理程序退出。如果您将参数声明为引用类型,那么您将获得对异常对象的引用,如果您重新抛出异常,它将始终引用同一个对象。

throw; 重新抛出实际上并没有对 catch 子句的参数做任何事情。它只是导致异常处理继续搜索 catch 子句和堆栈展开。异常对象还是一样

如果处理程序是 catch(...),这不会有任何变化。处理程序中没有要初始化的变量(引用与否),但 throw; 将重新抛出并继续搜索具有相同异常对象的处理程序。

当处理程序退出而不重新抛出异常时,异常对象将被销毁。对于 B 异常对象,这发生在 outer-most catch 的末尾,对于 A 异常,这发生在 inner-most catch 的末尾目的。 (你得到两次对 A 的析构函数调用,一次调用 catch 参数,一次调用异常对象。)