C++填坑系列:tip8 别让异常逃离析构函数

隐藏

前言

时隔1个月才续上tip 8的内容,这是因为要更好地理解这一部分内容,需要汇编的相关知识,于是顺便看了王爽的《汇编语言》以及《深入理解计算机系统》。而且生病了,耽误了10天时间,罪过啊。

预备知识

程序栈的结构

参考以下链接的前半部分:

参考资料: C++ 异常 与 ”为什么析构函数不能抛出异常“ 问题

SEH

SEH(Structured Exception Handling)是Win32为处理异常而特定安排的异常句柄数据结构,其应该为一个列表,而列表指针则存放在FS:[0]处。所以访问异常处理句柄先找到该指针FS:[0]

关于FS寄存器,具体参见: [转载]寄存器总结 level 2

别让异常逃离析构函数

这里用一个故事作为比喻来说明为什么不能让异常逃离析构函数:

假设你正在玩一款游戏:

  • throw抛出异常:你玩的游戏出现了某个故障,无法继续除非读档修复
  • 异常处理句柄SEH:《游戏攻略》这本书的目录,当游戏出现问题时,会翻开此目录,沿着目录检索解决的办法
  • 你写的catch:《游戏攻略》对应章节的解决办法,每个解决办法都要以前对应关卡才能完成,于是要读档回退到对应关卡。
  • windup(栈展开)栈展开:读档,时光机,回到过去,回到之前的关卡

程序如何做到较完美地回到过去?

对于栈,比较简单,栈指针回退到之前的位置即可,PC指针回到对应位置

然而主要注意的是:

(1) 对于比方说某些公共的资源,比如文件,sockcet,动态分配的内存,也需要归还。

打个比方,我在游戏中买了车,我读档坐着时光机回到过去的时候,发现这辆车已经被“某个人”征用了,我无法使用,这就不合理。

(2) 为了有序地,正确的归还资源,最好是按照分配的逆序方式,并且谁分配的,谁归还(例如C++的RAII)。

为何不能让异常逃离析构函数

假设你在游戏中买了辆车,某天换了个新的引擎,结果车开到一半出了故障(throw error),于是,你查看《游戏攻略》目录,上面写到需要时光倒流到买引擎之前的状态,于是你给引擎的供货商打电话(析构函数中退回引擎资源)。

然而电话占线了(析构过程中又出现异常),正常情况比如等待几分钟再打电话,然而,你查看《游戏攻略》,上面写道:出现给供货商打电话不通的情况,首先回退到很久远的版本(因为处理此异常的catch语句在很前面),于是时光倒流之后,发现当初仅仅因为电话没打通,车也没了,什么都没了,这显然是不合理。

-----EOF-----

Categories: c++ Tags: c++