《深度探索C++对象模型》读书笔记[第七章:站在对象的顶端]
7.1 Template
7.1.1 Template 的 "具现"行为
C++ Standard 已经禁止编译器将"声明一个指针指向某个 template class" " 具现 "出来.如果不是指针,而是引用,则会将template"具现"出来.
一个 class object 的定义 都会导致 template class 的"具现",也就是说真正对象布局会被产生出来.然而, member functions(至少对于那些未被使用过得)不应该被"实体"化.只有在member functions 被使用的时候,c++ STANDARD 才要求他们被"具现"出来.
7.1.2 Template 的错误报告
template的声明被手机称为一系列的"lexical tokens",而 parsing 操作延迟,直到真正有具现操作发生时才开始.每当看到一个 instantiation 发生,这组 token 就被推往parser,然后调用类型检验.
7.1.3 Template 中的名称决议方式
Template 之中, 对于一个 nonmember name 的决议结果是根据这个 name 的使用是否与"用以具现出该 template 的参数类型"有关而决定的.如果其使用互不相关,name就以"scope of the template declaration"来决定name.如果其使用互有关联,那么就以"scope of the template instantiation"来决定name.
编译器必须保持两个 scope contexts:
- "scopte of the template declaration",用以专注于一般的 template class。
- "scope of the template instantiation", 用以专注于特定的实体.
便一去的决议(resolution)算法必须决定哪一个才是适当的scope,然后在其中搜寻适当的 name .
7.1.4 Member Function 的具现行为
7.2 异常处理
7.2.1 Exception Handling 快速检阅
C++ 的 exception handling 由三个主要的语汇组件构成:
- 一个 throw 子句.它在程序某处发出一个 exception.被丢出去的 exception 可以是内建类型,也可以是使用者自定类型.
- 一个或多个 catach 子句。每一个 catch 子句都是一个 exception handler.它用来表示说,这个子句准备处理某种类型的 exception,并且在封闭的大括号区段中提供实际的处理程序.
- 一个 try 区段。它被围绕以一系列的叙述句(statements),这些叙述句可能会引发 catch 子句起作用.
当一个 exception 被丢出去时,控制权会从函数调用中被释放出来,并寻找一个温和的catch 子句。如果都没有吻合者,那么默认的处理例程 terminate() 会被调用.当控制权被放弃后,堆栈中的每一个函数调用也就被推离(popped up).这个程序称为 unwinding the stack。在每一个函数被推离堆栈之前,函数的local class objects 的 destructor 会被调用。
7.2.2 对 Exception Handling 的支持
当一个 exception 发生时,编译系统必须完成一下事情:
- 检验发生 throw 操作的函数;
- 决定 throw 操作是否发生在 try 区段中;
- 若是,编译系统必须把 exception type 巴莱和每一个 catch 子句比较;
- 如果比较温和,流程控制应该交到 catch 子句手中;
- 如果 throw 的发生并不在 try 区段中,或没有一个 catch 子句吻合,那么系统必须(a)摧毁所有 active local objects, (b) 从对战中将当前的函数"uwind"掉,(c)进行到程序堆栈中的下一个函数中去,然后重复上述步骤2~5.
7.3 执行期类型识别(RTTI)
7.3.1 Type-Safe Downcast(保证安全的向下转型操作)
想要支持 type-safe downcast,在 object 空间和执行时间上都需要一些额外负担:
- 需要额外的空间以存储类型信息(type information),通常是一个指针,指向某个类型信息节点.
- 需要额外的时间以决定执行器的类型(runtime type),因为,正如其名所示,这需要在执行器才能决定.
RTTI实现方式为:
经由一个或多个 virtual functions 来区别 class 声明.其有点事透明化地将旧有程序转换过来,只要重新编译就好.缺点则是可能会将一个其实并非必要的virtual function 强迫导入继承体系的 base class 身上.从编译器的角度来看,这个策略还有其它优点,就是大量降低额外负担.所有 polymorphic classes的 objects 都维护了一个指针(vptr),指向 virtual function table. 只要我们把与该 class 相关的RTTI object 地址放进 virtual table 中(通常放在第一个 slot),额外负担就降低为: 每一个 class object只多话费一个指针。这个指针只需被设定一次,它是被编译器静态设定,而不是在执行期由 class constructor 设定(vptr才是这么设定)
7.3.2 Type-Safe Dynamic Cast(保证安全的动态转型)
dynamic_cast 运算符可以在执行期决定真正的类型.如果downcast 是安全的,这个运算符会传回被适当转型过的指针.如果downcast不是安全的,这个运算符会传回0.
type_info 是 C++ Standard 所定义的类型描述器的 class 名称,该 class 中放置着待所求的类型信息. Virtual table 的第一个 slot内含type_info object 的地址;
7.3.3 References 并不是Pointers
指针可以通过dynamic_cast传回的地址是否为0来判断向下转型是否成功。但是references不行,因为一个reference 不可能像指针那样"把自己设为0便代表了"no object""。失败了怎么办?答案是丢出一个 bad_cast exception.
7.3.4 typeid 运算符
typeid 运算符传回一个 const reference,类型为 type_info。
编译器必须提供的最小量信息是 class 的真是名称,以及在 type_info objects 之间的某些排序算法(before()),以及某些形式的描述器.
7.4 效率有了,弹性呢
动态共享函数库
共享内存