- 索引
- 1、Template
- 1.1 emplate实例行为
- 1.2 template中名称决议法
- 1.3 成员函数的实例化行为是什么?
- 2、异常处理(Exception Handling)
- 2.1 EH简介
- 2.2 对EH的支持
- 3、执行期类型识别
- 3.1 保证安全的向下转换操作
- 3.2 dynamic_cast
- 3.3 typeid
C++【对象模型】| 【01】简单了解C++对象模型的布局
C++【对象模型】|【02】构造函数何时才会被编译器自动生成?
C++【对象模型】|【03】拷贝构造是如何工作的,何时才会用到呢?
C++【对象模型】 | 【04】程序在内部被编译器如何转化?
C++【对象模型】 | 【05】类与类之间各种关系下对数据成员的存取、绑定、布局
C++【对象模型】| 【07】构造、析构、拷贝做了哪些事?
C++【对象模型】| 【08】类在执行期会处理哪些事呢?
C++【对象模型】| 【09】类模板、异常处理及执行期类型识别
三种扩充性质将会影响C++对象: template、exception handing、runtime type identification;1、Template
Template:该模板表达式将在编译时期而非执行期被评估,将给程序带来重大的效率提升; 但同时也带来缺点,当与其像依赖的头文件,若产生修改将会重新编译;1.1 emplate实例行为
【实例化】表示进程将真正的类型和表达式绑定到template相关形式参数上的操作;
当编译器看到templat class 声明时,会如何处理呢?
在实际程序中,它却没有反应;class中的每一个都只能通过该类模板的实例来调用; template::freeList; // 可通过尖括号中不同类型创建出不同的实例 【指针却在不同类型下不会产生多个实例?】 - 由于指向一个类对象的指针本身不是一个类对象,故该指针不需要与对象相关的任何数据,故不用将其实例化,但C++标准禁止这样处理; 【上述为指针,那么引用呢?】 - 它将会被实例化,否则将会报错;
如何让成员函数需要使用的才进行实例化,编译器不会这样要求,需要使用者来主导,为什么呢?
- 空间上和时间上的考虑,如果类中有大量成员函数,但实际用到只有几个,那么将会耗费大量时间和空间; - 尚未实现的机能,主要实例化真正用到的成员函数即可;
Point*p = new Point ; 需要实例化的有:Point的float类型、new、默认构造三种; 【此类函数在何时将会被实例化呢】 - 如果在编译时,函数将实例化在Point原文件和p所在文件中; - 如果在链接时,编译器会被重新激活,将template函数实例放在其他文件中;
于template内,什么样的编译器会在编译器处理template声明时被标示出来?
如cfront对template完全解析但不做类型检验,在每一个实例化操作发生时才做类型检验;1.2 template中名称决议法
extern double foo(double); templateclass ScopeRules { public: void invariant() { _member = foo(_val); // foo不是取决于模板参数 } type type_dependent() { return foo(_member); // foo调用却决模板参数 } private: int _val; type _member; }; extern int foo(int); ScopeRules sr0;
分为两种:①定义出template的程序端、②实例化template程序端; - ①用以专注于一般的模板类; - ②用于专注特定的实例; 上述中两个foo()调用,在程序定义前有一个foo声明,而在实例化前有两个foo声明;
当使用sr0.invariant()时,内部的foo将调用foo(double);这是为什么呢?
template中,对一个非成员名称(foo)的决议根据name是否与用以实例化该template的参数类型有关而决定,若不相关,则以①来决定,相关则用②; 而该foo()与实例化该类的参数类型无关【代码注释】;1.3 成员函数的实例化行为是什么?
目前实例化有两种行为: - 【编译】,code必须在程序文本文件中; - 【链接】,辅助工具来引导编译器实例化; 【编译器如何找出函数定义】 - 其中Borland从保护template程序文件中查找,或者要求一种文件用来才存放; 【编译器如何能够只实例化程序中用到的成员函数】 - 模拟链接操作,检测哪一个函数真正需要,在将其实例化; 【编译器如何阻止member definition在多个.o文件中都被实例化呢?】 - 产生多个实例,在从链接器中提供支持,只留下其中一个实例,其余的忽略; - 或者由使用者来引导模拟链接阶段的实例化策略,决定哪些实例才是需要的; cfront3.0提供一个由使用者驱动的自动实例化机制; 【directed-instantiation机制】 - 一个程序的原始码被编译时,最初不产生任何实例,但其相关信息将被产生于类文件中; - 当类文件链接一起时,一个prelinker程序将被执行,用于检查对象文件; - 对每一个参考到template实例而该实例却没有定义时,prelinker将必要的程序实例化操作指定给特定文件(.ii文件); - prelinker重新执行编译器,编译.ii文件中将其都实例化; - 所有的对象文件都被链接成一个可执行文件; - 该机制的主要成本在于第一次编译时.ii文件的设定时间,及次要的对每一个编译后执行prelinker; 【当一个类被用于几十个文件中,编译器如果确保只有一个虚表实例被产生?】 - 每个虚函数的地址都被置于active classes的虚表中; - 若取得函数地址,表示虚函数定义必定出现在程序的某个地点,否则链接失败; - 且改函数只能由一个实例,否则链接失败; - 故将其放入定义改类的第一个非内联、非纯虚函数的文件中; - 虚函数被实例化,在类的实例化点之后;2、异常处理(Exception Handling)
支持EH,则编译器将要找出catch子句,且需要跟踪程序堆栈中每一个函数做作用区域;如果是类EH,则可能要负责其虚构、清理等动作; EH需要编译器产生的数据结构和执行期的一个exception library: - 为了维护执行速度,则需要在编译时间构建数据结构,这膨胀程序大小; - 为了维护程序大小,则需要在执行期建立数据结构,这会影响执行速度; - 故编译器必须在上面做出抉择;2.1 EH简介
EH由以下三个组成: - throw子句,发出一个exception; - catch子句,没一个子句就是一个EH,处理某种类型的exception; - try区段; 当一个exception抛出时,控制权会从函数调用中被释放出来,寻找catch子句;2.2 对EH的支持
当一个exception发生时,系统会执行: - 检验发生throw操作的函数; - 决定throw操作是否发生在try区段; - exception type域catch子句比较; - 交到catch子句;
如何将exception type和每一个catch type比较
编译器必须为此产生一个类型描述器(如PTTI),对exception的类型进行编码;3、执行期类型识别
class node {};
class type : public node {};
class fct : public type {};
class gen : public type {};
void simplify_conv_op(type pt) {
fct pf = fct(pt);
}
在cfont2.0前conversion运算符不能被重载,且上述函数(simplify_conv_op)内转换也是安全的;3.1 保证安全的向下转换操作
缺乏一个保证安全的向下转换,需要额外负担: - 需要额外的空间以存储类型信息; - 需要额外的时间以决定执行期的类型; 如何解决向下转化: - 使用关键词dynamic_cast; - 通过声明一个或多个虚函数来区别class声明;3.2 dynamic_cast
该运算符可以在执行期决定真正的类型; 使用它,是取出vptr[0]的类型描述器信息进行转化;
将该运算符使用在reference上
当使用指针时,转换失败将会被设置为0,而引用却不行,若设置为0,将会引起临时性对象被产生出来; 那会如何处理呢? 如果转换失败,则将会抛出一个bad_cast exception;3.3 typeid
该运算符在执行期查询某个类型的信息;



