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
参数,一次调用异常对象。)
我知道 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
参数,一次调用异常对象。)