Template:
Template 的二个问题:
1. template 的声明。当编译器看到一个template 声明时,编译器什么反应都没有。一切都不可用,只有通过template 的某个实体来存取或操作。
2. template 的具现。
Point < float > fPoint;//产生一份实体与Point class 的float instantiation 在程序中产生关联。
Point < float > *ptr = 0;//程序什么也没有发生,因为一个指向class object 的指针,本身不是一个class object。不需要具现一个float 实体。
Point < float > &ref = 0;//会具现出一个“Point 的float 实体”,他的真正语意是:
Point < float > temporary ( float ( 0 ) );
Const Point < float > &ref = temporary;
因为reference 并不是无物的代名词。0被视为整数,必须被转换为类型为Point < float > 的一个对象。如果没有转换的可能,这个定义就是错误的,会在编译时被挑出来。
C++ Standard 让使用者来主导“具现”规则,有两个主要原因:
空间和时间效率的考虑。Member functions 的具现时间有使用者决定。
尚未实现的机能。有些用到的运算符可能一些具现的实体没有定义,由使用者来主导“具现”规则,template 就能够支持那些本来可能造成编译时期错误的类型。
Template 机制使类型检验被拖到“具现”操作发生时才进行,因而导致很多十分漏骨的错误声明都能通过编译。
Template 之中,对于一个nonmember name 的决议结果是根据这个name 的使用是否与“用以具现出该template 的参数类型”有关而决定的。如果其使用互不相关,那么就以“定义出template 的程序”来决定name。如果其使用互有关联,那么就以“具现出template 的程序”来决定name。
这意味着一个编译器必须保持两个scope contexts:scope of the template declaration;scope of the template instantiation.
Member Function 的具现行为:
Member functions 可能在编译时期或链接时期被具现出来。
编译器必须回答三个问题:
编译器如何找出函数的定义?
Ø 方法一:包含template program text file,就好象它是个header 文件一样。
Ø 方法二:要求一个文件命名规则:在.h文件中发现的函数声明,必须在.c 或.cpp中找到template program text。
编译器如何能够只具现出程序中用到的member functions?
Ø 方法一:把所有的member functions 都产生出来。
Ø 方法二:仿真链接操作,检测看看哪一个函数真正需要,然后只为它(们)产生实体。
编译器如何阻止 member definitions 在多个.o 文件中都被具现呢?
Ø 方法一:产生多个实体,然后从链接器中提供支持,只留下其中一个实体,其余都忽略。
Ø 方法二:由使用者来导引“仿真链接阶段”的具现策略,决定哪些实体才是所需求的。
Template 机制的各种实现的弱点就是当实体第一次被产生出来时或是被非必要的再次具现或是“决定那些函数是否需要具现”所花的代价太大是,会大量增加编译时间,编译器的表现另人失望。
C++语言规定:如果一个virtual function 被具现出来,其具现点紧跟在其class 的具现点之后。
C++ Standard 已经扩充了对template 的支持,允许程序员明确地要求在一个文件中将整个class template 具现出来
Template class Point3d < float >;
或是针对一个template class 的个别member function:
template float Point3d < float >::x() const;
或是针对某个个别template function:
template Point3d < float > operator+( const Point3d < float >&, const Point3d < float >& );
异常处理
Exception Handling 包括三部分:
1. 一个throw 子句。它在程序某处发出一个exception。被丢出去的exception 可以是内建类型,也可以是使用者自定类型。
2. 一个或多个catcha 子句。每一个catch 子句都是一个exception handler。它用来表示说,这个子句准备处理某种类型的exception,并且在封闭的大括号区段中提供时间的处理程序。
3. 一个try 区段。它被围绕以一系列的叙述句。这些叙述句可能会引发catch 子句起作用。
当一个exception 被丢出去时,控制权会从函数调用中被释放出来,并寻找一个吻合的catch 子句。如果都没有吻合者,那么默认的处理例程terminate() 会被调用。
当一个exception 发生时,编译系统必须完成以下事情:
1. 检验发生throw 操作的函数;
2. 决定throw 操作是否发生在try 区段中;
3. 若是,编译系统必须把exception type 拿来和每一个catch 子句比较;
4. 如果比较吻合,流程控制应该交到catch 子句手中;
5. 如果throw 的发生并不在try 区段中,或没有一个catch 子句吻合,那么系统必须(a)摧毁所有active local objects,(b)从堆栈中将当前的函数“unwind”掉,(c)进行到程序堆栈中的下一个函数中去,然后重复上述步骤2~5。
执行期类型识别
C++ 的RTTI 机制提供了一个安全的downcast 设备,但是只对那些展现“多态”的类型有效。但是怎么分辨一个class 是否具有“多态”性质呢?在C++ 中,一个具备多态性质的class,正是内含着继承而来的virtual functions。因此可以把与该class 相关的RTTI object 地址放进virtual table 中(通常放在第一个slot中)。
Type-safe Dynamic Cast(保证安全的动态转型):
Typedef type *ptype;
Typedef fct *pfct;
Simplify_conv_op (ptype pt)
{
if (pfct pf = dynamic_cast < pfct > ( pt ) ) {
// …process pf
}
else {…}
}
dynamic_cast 的真正成本是:pfct 的一个类型描述器会被编译器产生出来。由pt 指向之class object 类型描述器必须在执行期通过vptr取得。下面是可能的转换:
( ( type_info* ) ( pt->vptr [ 0 ] ) ) -> _type_descriptor;
type_info 是C++ Standard 所定义的类型描述器的class 名称,该class 中放置着待索求的类型信息。
对一个class指针类型施以dynamic_cast 运算符,会获得true 或 false:
n 如果传回真正的地址,表示这个object 的动态类型被确认了,一些与类型有关的操作现在可以施行于其上。
n 如果传会0,表示没有指向任何object,意味应该以另一种逻辑施行于这个动态类型未确定的object 身上。
对一个class 引用类型施以dynamic_cast 运算符,会获得true 或 bad_cast exception.
n 如果reference 真正参考到适当的derived class (包括下一层或下下一层或…),downcast 会被执行而程序可以继续进行。
n 如果reference 并不真正是某一种 derived class,那么,由于不能够传回0,遂丢出一bad_cast exception。
Typeid 运算符:
Typeid( expression )或 Typeid ( type ) 返回一个const reference,类型为tyoe_info.
Simplify_conv_op ( const type &rt )
{
if ( typeid ( rt ) = = typeid ( fct ) )
{
fct &rf = static_cast < fct& > ( rt );
//…
}
else {…}
}