直接说结论:throw的表达式创建出来的变量会被拷贝下来【通过拷贝构造函数,后面会证实这一点,且是放在堆里的】,然后沿着调用路径去搜索最近匹配异常的catch语句,在沿途,传递给catch语句的是堆中的异常变量的拷贝;
下面来证明一件事:
1:异常的传递是靠拷贝进行的。
看代码:
#include<iostream>
#include<memory>
using namespace std;
class m_exception :public exception {
public:
m_exception(const char* message)
:exception(message){
cout << this << endl;
}
void printAddress() {
cout << this << endl;
}
~m_exception() {}
};
void f() {
m_exception e= m_exception("e");
throw e;
}
void g() {
try {
f();
}
catch (m_exception e) {
e.printAddress();
throw;
}
}
int main() {
try {
g();
}
catch (m_exception e) {
e.printAddress();
}
}
运行结果:
可以看见,每个catch语句中的变量地址都是不同的,所以这能说明传递异常的方式是拷贝。
如果我们是将拷贝构造函数delete掉的话:
m_exception(const m_exception e) = delete;
编译器就会报错:
这也能说明这是变量是靠拷贝构造函数传递的;
下面来证明两件事
1:最开始throw异常的的表达式会将异常拷贝到堆里最后由系统回收;
2:每次拷贝的不是下一层传递来的异常,而是存储在堆中的原始异常;
先来证明1:
#include<iostream>
#include<memory>
using namespace std;
class m_exception :public exception {
public:
int x = 100;
m_exception(const char* message)
:exception(message){
cout << this << endl;
}
void printAddress() {
cout << this << endl;
}
~m_exception() {}
};
void f() {
m_exception e= m_exception("e");
throw e;
}
void g() {
try {
f();
}
catch (m_exception e) {
e.x = 1000;
cout << e.x << endl;
e.printAddress();
throw;
}
}
int main() {
try {
g();
}
catch (m_exception e) {
cout << e.x << endl;
e.printAddress();
}
}
运行结果:
可以看到,虽然我们在g中修改了x,但是并没有影响到main中的异常,所以上一级的环境并不是拷贝下一级环境的异常;
当我们将catch的参数改为引用时:
void g() {
try {
f();
}
catch (m_exception& e) {
e.x = 1000;
cout << e.x << endl;
e.printAddress();
throw;
}
}
int main() {
try {
g();
}
catch (m_exception& e) {
cout << e.x << endl;
e.printAddress();
}
}
运行结果:
可以看到,两个异常的地址相同,且g中的修改能影响到main;
综上所述,我们完全有理由肯定,每一层的catch语句都是捕获属于其本身的原异常的拷贝副本;
我们知道,c++的存储空间可以分为栈空间和堆空间,当我们的能在不同函数之间安全传递的变量只能是堆空间上的变量,所以我们也有充分的理由信息,这个变量是在堆上的;
证明完毕;