这个章节我们主要学习一下几个知识点:
1.异常是如何实现的?
2.异常是如何销毁局部变量的?它怎么知道哪些临时变量需要销毁?
3.异常效率真的很低?
1.前言 责任链设计模式
如果你能够理解设计模式中的责任链模式,那么你对异常原理模型也就吃透一大半了。什么,有这么神奇?或许你会问,这是个啥套路咋没有见过。那我形象、具体、简单的给你举个例子。在高中的化学课程中,你是不是经常做到这样的题目从一种液体中将各种化学元素分离出来。还记得是怎么做的?是不是将液体不断倒入各种试剂中反应,生成其他物质,然后分离出来的。
简单画了一张图帮助大家理解下。在整个流入的方向中,我们把自己感兴趣的元素给拦截下来,不感兴趣的就让其通过,走到下一个关卡。然后在重复同样的操作,一直到最后。
前面费了口舌简单解释了下责任链设计模式,或许你心里直打鼓,真的这么简单?但是这和异常处理有什么?
这里请允许我卖个关子,我们继续阅读下去。
2.异常所需要的数据结构
这次我们只站在上层应用的角度看异常是这么实现的,并不会太关注操作系统底层。我们只要用好C++封装好的就行。
在分析具体的实现原理之前,我们先思考两个问题。
1.捕捉异常和抛出异常,他们两个之间是怎么联系起来的。你怎么知道我抛出是个啥,又是在哪个catch段中处理的。你想想在谍战片中两个特务是怎么接头的,是不是一个说上句,另外一个人说下局。啪,暗号对上了。哦,原来是自己人自己人。这个暗号,其实就是什么呢,说的再通俗一点就像是古代的信物。
那么在异常里面这个信物是什么呢。
2.编译器为了实现这个异常,肯定是加了不少的数据结构,用来支撑背后所需要实现的功能。
好了,我要上数据结构了,不要慌,看看就好,大家不感兴趣的直接跳到2.3节。
2.1异常安装部分结构
funcInfo |
struc |
; (sizeof=0x14) |
magicNumber |
dd |
;编译器生成标记固定数字0x19930520 |
maxState |
dd |
;最大栈展开数的下标值 |
pUnwindMap |
dd |
;指向栈展开函数表的指针,指向UnwindMapEntry表结构 |
dwTryCount |
dd |
; try块数量 |
pTryBlockMap |
dd |
;try块列表,指向TryBlockMapEntry表结构 |
nIPMapEntries |
dd |
|
pIPtoStateMap |
dd |
|
pESTypeList |
dd |
|
EHFlags |
dd |
|
FuncInfo |
ends |
UnwindMapEntry |
struc |
; ( sizeof=0x08) |
toState |
dd |
;栈展开数下标值 |
lpFunAction |
dd |
;展开执行函数 |
UnwindMapEntry |
ends |
TryBlockMapEntry |
struc |
; (sizeof=0x14) |
tryLow |
dd |
; try块的最小状态索引,用于范围检查 |
tryHigh |
dd |
;try块的最大状态索引,用于范围检查 |
catchHigh |