- 7.1 Template
- 7.2 异常处理(Exception Handing)
- 7.3 执行期类型识别(Runtime Type Identification,RTTI)
- 7.4 效率有了,弹性呢?
- 有三个著名的C++语言扩充性质,它们都会影响C++对象。分别是template、exception handing(EH)和runtime type identification(RTTI)(RTTI可以想象成EH的一个副作用)。
7.1 Template
- template原本被视为是对container classes的支持,但现在成为STL的基础。它也被用于属性混合(如内存分配机制)或互斥(mutual exclusion)机制(使用于线程同步化控制)。它设置被用于template metaprograms技术:class expression templates将在编译时期而非执行期被评估(evaluated),因而带来效率提升。
Template的“实例化”行为(Template Instantiation)
- 下面的template Point class: 实际程序中,static data members并不可用,nested enum或enumerators也一样。它们每个只能通过template Point class的某个实例来存取或操作:
1
2
3
4
5
6
7
8
9
10
11
12
13
14template<class Type>
class Point {
public:
enum Status { unallocated, normalized };
Point(Type x = 0.0, Type y = 0.0, Type z = 0.0);
~Point();
void* operator new(size_t);
void operator delete(void*, size_t);
// ...
private:
static Piont<Type> *freeList;
static int chunkSize;
Type _x, _y, _z;
};如果定义一个指针,指向特定实例:1
2
3
4// ok
Point<float>::Status s;
// error
Point::Status s;什么也没发生。因为指向class object的指针,本身并不是class object,编译器不需要直到与该class有关的任何members数据或object布局数据。如果不是pointer而是reference,又如何:1
Point<float> *ptr = 0;
所以一个class object的定义,不论是由编译器暗中地做或是程序员显式地做,都会导致template class的实例化。也就是Point中的三个nonstatic members都会被绑定。然而member functions(未被使用过的)不应该被实例化。只有在需要时才实例化。但编译器不遵循这项要求,所以由使用者来主导实例化(instantiation),主要有两个原因:1
2
3
4const Point<float> &ref = 0;
// 内部扩展
Point<float> temporary(float(0));
const Point<float> &ref = temporary;- 空间和时间效率的考虑。
- 尚未实现的机能。
Template的错误报告(Error Reporting within a Template)
Template中的名称决议法(Name Resolution within a Template)
- template的两种意义: Template中,一个nonmember name的决议结果,是根据name的使用是否与实例化该template的参数类型有关而决定的。如果无关,就以scope of the template declareation决定name,否则以scope of the template instantiation决定name。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23// scope of the template definition
extern double foo(double);
template<class type>
class ScopeRules
{
public:
void invariant() {
_member = foo(_val);
}
type type_dependent() {
return foo(_member);
}
// ...
private:
int _val;
type _member;
};
// scope of the template declareation
extern double foo(double);
// scope of the template instantiation
extern int foo(int);
// ....
ScopeFules<int> sr0;
ScopeFules template中有两个foo()调用操作。它会调用foo(double)的版本,此外,_val的类型是int。因为函数的决议结果只和函数的原型(signature)有关,和函数的返回值没有关系。因此_member的类型不会影响哪一个foo()实例被选中。在scope中,只有一个foo()候选者。调用操作由scope of the template declareation决议。如果按以下方式:它由scope of the template instantiation决议。它会调用int版本,被type把持。1
sr0.type_dependent();
这意味着编译器必须保持两个scope contexts:- “scope of the template “,用于专注一般的template class。
- “scope of the template instantiation”,用于专注特定的实例。
Member Function的实例化行为(Member Function Instantiation)
- 对于template function的实例化(instantiation)。编译器提供了两个策略:一个是编译时期策略,程序代码在program text file中备妥可用;另一个是链接时期策略。
- 如果virtual function被实例化(instantiated),其实例化点紧跟在class的实例化点后。
- template instantiation似乎拒绝全面自动化,虽然工作做对了,但产生出来的object files重新编译成本可能太高,以手动方式在个别object module中完成预先实例化操作(pre-instantiation)是唯一有效率的方法。
7.2 异常处理(Exception Handing)
- 想要支持exception handing,编译器的主要工作就是找出catch子句,以处理被抛出的exception。这需要追踪程序堆栈中的每个函数的目前作用区域,同时编译器需要提供某种查询exception objects的方法,以直到实际类型(RTTI)。还需要某种机制管理被抛出的object,包括产生、存储、析构、清理和一般存取。
Exception Handing快速检阅
- exception handing由三个主要组件构成:throw子句、catch子句和try区段。当一个exception被抛出去,控制权会从函数调用中释放出来,并寻找吻合的catch子句。如果没有,则调用terminate()中断例程。当控制权被放弃后,堆栈中每个函数调用也被推理(popped up)。
对Exception Handing的支持
- 当exception发生时,编译系统完成以下事情:
- 检验发生throw操作的函数。
- 决定throw操作是否在try区段中。
- 把exception type拿来和每个catch子句比较。
- 流程控制交到吻合的匹配的catch子句手中。
- 如果throw不发生在try区段中,或没有catch子句吻合。那么:
- 摧毁active local objects。
- 堆栈中将函数unwind掉
- 进行堆栈的下一个函数,然后重复2·5。
决定throw是否发生在一个try区段中
- 一个函数可以想象为好几个区域:
- try区段以外的区域,没有active local objects。
- try区段以外的区域,但有一个(或以上)的active local objects需要析构。
- try区段以内的区域。
将exception的类型和每一个catch子句的类型做比较
- 对于每个被抛出来的exception,编译器产生一个类型描述器,对exception的类型进行编码。如果那是derived type,编码内容包含所有base class的类型信息。
- 类型描述器(type derscriptor)是必要的。因为exception是在执行期被处理的,object必须有自己的类型信息。RTTI正是因为支持EH而获得的副产品。