先上代码

实例1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<iostream>
using namespace std;
class B{
public:
    ~B(){
        cout<<"B"<<endl;
    };
};
int main(){
    try{
        B b;
        throw B();//扔出一个临时对象
    }
    catch(const B &b){
        cout<<"end"<<endl;
    }
}
/*
测试结果
B
end
B
*/

结论:抛出异常后,首先析构从try语句内的局部变量,然后再执行catch语句,执行完后,再执行对throw后面对象的析构

实例2:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include<iostream>
using namespace std;
class B{
public:
    ~B(){
        cout<<"B"<<endl;
    };
};
int main(){
    try{
        B b;
        throw b;
    }
    catch(const B &b){
        cout<<"end"<<endl;
    }
}
/*
测试结果
B
end
B
*/
//将&b改成b,则执行结果为
/*
B
end
B
B
*/

结论:throw后使用了b对象,实际上是对b对象的一次拷贝,所以析构的先析构b对象,再析构b对象的副本,析构了两次

实例3:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include<iostream>
using namespace std;
class B{
public:
    ~B(){
        cout<<"B"<<endl;
    };
};
class A{
public:
    ~A(){
        cout<<"A"<<endl;
    };
};
class C{
public:
    ~C(){
        cout<<"C"<<endl;
    };
};
int main(){
    try{
        C c;
        {
            B b;
            A a;
            throw b;
        }
    }
    catch(const B &b){
        cout<<"end"<<endl;
    }
}

//测试结果
/*
A
B
C
end
B
*/

结论:按声明时相反的顺序先析构掉本层作用域内的对象,再往上析构掉高层作用域中的对象,依次上推直到将throw之前的在本try语句块中声明的所有对象都析构掉为止

总结

throw的时候将被抛出的对象拷贝一份到专用的异常栈上,接着按声明时相反的顺序先析构掉本层作用域内的对象,再往上析构掉高层作用域中的对象,依次上推直到将throw之前的,在本函数中声明的所有对象都析构掉为止。然后再执行catch中的相关语句,最后才析构掉拷贝对象。