快速业务通道

C++编译器如何实现异常处理

作者 佚名技术 来源 程序设计 浏览 发布时间 2012-06-30
出:堆栈展开工作必须调用异常发生时所有生存的局部对象的析构函数。如下面的代码:
int g_i = 0;
void foo()
{
  T o1, o2;
  {
    T o3;
  }
  10/g_i; //这里会发生异常
  T o4;
  //...
}
  foo有o1、o2、o3、o4四个局部对象,但异常发生时,o3已经“死亡”,o4还未“出生”,所以异常处理程序应该只调用o1和o2的析构函数。

前面已经说过,编译器会在函数的很多地方安插代码来记录当前的运行状态。实际上,编译器在函数中设置了一些关键区域,并为它们分配了id,进入关键区域时要记录它的id,退出时恢复前一个id。try块就是一个例子,其id就是start id。所以,在try块的入口,编译器会把它的start id记到栈桢上去。局部对象从创建到销毁也确定了一个关键区域,或者,换句话说,编译器给每个局部对象分配了唯一的id,例如下面的程序:

void foo()
{
  T t1;
  //.
}
编译器会在t1的定义后面(也就是t1创建以后),把它的id写到栈桢上:
void foo()
{
  T t1;
  _id = t1_id; //编译器插入的语句
  //.
}
  上面的_id是编译器偷偷创建的局部变量,它的位置与EXCEPTION_REGISTRATION的id字段重叠。类似的,在调用对象的析构函数前,编译器会恢复前一个关键区域的id。
  清理栈桢时,异常处理程序读出id的值(通过EXCEPTION_REGISTRATION结构的id字段或栈桢指针EBP下面的4个字节来访问)。这个id可以表明,函数在运行到与它相关联的那个点之前没有发生异常。所有在这一点之前定义的对象都已初始化,应该调用这些对象中的一部分或全部对象的析构函数。请注意某些对象是属于子块(如前面代码中的o3)的,发生异常时可能已经销毁了,不应该调用它们的析构函数。

编译器还为函数生成了另一个数据结构——堆栈展开表(unwindtable,我启的名字),它是一个unwind结构的数组,可通过funcinfo来访问,如图4所示。函数的每个关键区域都有一个unwind结构,这些结构在展开表中出现的次序和它们所对应的区域在函数中的出现次序完全相同。一般unwind结构也会关联一个对象(别忘了,每个对象的定义都开辟了关键区域,并有id与其对应),它里面有如何销毁这个对象的信息。每当编译器碰到对象定义,它就生成一小段代码,这段代码知道对象在栈桢上的地址(就是它相对于栈桢指针的偏移),并能销毁它。unwind结构中有一个字段用于保存这段代码的入口地址:
typedef void (*CLEANUP_FUNC)();
struct unwind
{
  int prev;
  CLEANUP_FUNC cf;
};
  try块对应的unwind结构的cf字段是空值NULL,因为没有与它对应的对象,所以也没有东西需要它去销毁。通过prev字段,这些unwind结构也形成了一个链表。异常处理程序清理栈桢时,会读取当前的id值,以它为索引取得展开表中对应的项,并调用其第二个字段指向的清理代码,这样,那个与之关联的对象就被销毁了。然后,处理程序将以当前unwind结构的prev字段为索引,继续在展开表中找下一个unwind结构,调用其清理代码。这一过程将一直重复,直到链表的结尾(prev的值是-1)。图6画出了本节开始时提到的那段代码的堆栈展开表。

现在把new运算符也加进来,对于下面的代码:
T* p = new T();

系统会首先为T分配内存,然后调用它的构造函数。所以,如果构造函数抛出了异常,系统就必须释放这些内存。因此,动态创建那些拥有“有为的构造函数”的类型时,VC++也为new运算符分配了id,并且堆栈展开表中也有与其对应的项,其清理代码将释放分配的内存空间。调用构造函数前,编译器把new运算符的id存到EXCEPTION_REGISTRATION结构中,构造函数顺利返回后,它再把id恢复成原来的值。

更进一步说,构造函数抛出异常时,对象可能刚刚构造了一部分,如果它有子成员对象或子

凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!

分享到: 更多

Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved

地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008

电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134

《中华人民共和国增值电信业务经营许可证》闽B2-20100024  ICP备案:闽ICP备05037997号