直接说结论:throw的表达式创建出来的变量会被拷贝下来【通过拷贝构造函数,后面会证实这一点,且是放在堆里的】,然后沿着调用路径去搜索最近匹配异常的catch语句,在沿途,传递给catch语句的是堆中的异常变量的拷贝;
下面来证明一件事:
1:异常的传递是靠拷贝进行的。
看代码:
- #include
- #include
- 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
- #include
- 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++的存储空间可以分为栈空间和堆空间,当我们的能在不同函数之间安全传递的变量只能是堆空间上的变量,所以我们也有充分的理由信息,这个变量是在堆上的;
证明完毕;