note-cpp-Structure-Error-Handling

原文 : https://www.cnblogs.com/zhyg6516/archive/2011/03/08/1977007.html

文章 深入讨论了 带有异常处理的, 函数栈级别的解释, 待深入整理

NOTE: error handling

1. 当异常发生时,系统得到控制权,系统从FS:[0]寄存器取到异常处理链的头,以及异常的类型, 调用异常处理函数。(异常函数是编译器生成的)

2. 从链表头去匹配 异常类型和catch 块接收的类型。( 这里用到RTTIRTTI(Run-Time Type Identification) 信息)

3. unwind stack。这里需要析构已经创建的对象。( 这里需要判断析构哪些对象,这一步是编译器做的)

4. 执行catch 块代码。

后返回到程序的正常代码,即catch块下面的第一行代码。

insert : 栈展开 / 内存泄露 ( 待单独整理

因为栈展开的时候并不会自动对指针变量执行delete(或delete[])操作

因此,在有可能发生异常的函数中,在C++编程中,可以利用“智能指针”auto_ptr来防止内存泄露。参考如下程序

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
43
44
45
46
47
48
49
50
51
52
53
54
55
> 
> #include <memory>
> using namespace std;
>
> class {
> int num;
> public:
> A(int i):num(i){
> cout<<"this is A's constructor, num="<<num<<endl;
> }
> ~A(){
> cout<<"this is A's destructor, num="<<num<<endl;
> }
> void show(){
> cout<<num<<endl;
> }
> };
> 大专栏  note-cpp-Structure-Error-Handling
> void autoptrtest1(){
> A* pa=new A(1);
> throw 1;
> delete pa;
> }
>
> void autoptrtest2(){
> auto_ptr<A> pa(new A(2));
> pa->show();
> throw 2;
> }
>
> int main(){
> try{
> autoptrtest1();
> }
> catch(int){
> cout<<"there is no destructor invoked"<<endl;
> }
> cout<<endl;
> try{
> autoptrtest2();
> }
> catch(int){
> cout<<"A's destructor does be invoked"<<endl;
> }
> }
>
> 程序的输出结果:
> this is A's constructor, num=1
> there is no destructor invoked
>
> this is A's constructor, num=2
> 2
> this is A's destructor, num=2
> A's destructor does be invoked
>

在解读上面的这段程序的时候,要注意以下几点。 [1]

(1)在函数autoptrtest1()中,由于异常的发生,导致delete pa;无法执行,从而导致内存泄露。

(2)auto_ptr实际上是一个类模板,在名称空间std中定义。要使用该类模板,必须包含头文件memory。auto_ptr的构造函数可以接受任何类型的指针,实际上是利用指针类型将该类模板实例化,并将传入的指针保存在auto_ptr 对象中。

(3)在栈展开的过程中,auto_ptr 对象会被释放,从而导致auto_ptr 对象的析构函数被调用。在该析构函数中,将使用delete运算符将保存在该对象内的指针所指向的动态对象被销毁。这样,就不会发生内存泄露了。

(4)由于已经对*和->操作符进行了重载,所以可以像使用普通的指针变量那样使用auto_ptr 对象,如上面程序中的pa->show()。这样可以保留使用指针的编程习惯,方便程序猿编写和维护。

可见,在exception 找到对应的 Catche 块后, 去栈展开(unwind stack),析构已有的对象后,进入到Catch 块中。 问题是: 程序怎么知道程序运行到哪里? 哪些对象需要调用析构函数? 这也是编译器做的,对于每一个Catch 块,其记录下如果该catch 块若被调用,哪些对象需要被析构。 这有这么一张表。具体实现可以参见reference2.

猜你喜欢

转载自www.cnblogs.com/sanxiandoupi/p/11699000.html