目录
Type-Safe Downcast(保证安全的向下转换操作)
Type-Safe Dynamic Cast(保证安全的动态转换)
执行期类型识别:Runtime Type Identification,RTTI
在cfront中,用以表现一个程序所谓“内部类型体系”,看起来像这样:
class node {...};
//root of the 'type' subtree:basic types,
//'derived' types:pointers,arrays,
// functions,classes,enums ...
class type : public node {...};
//two representations for functions
class fct : public type {...};
class gen : public type {...};
其中gen是 generic的简称,用来表现一个overloaded function。
于是只要你有一个变量,或是类型type *的成员(并知道它代表一个函数),你就必须决定其特定的derived type是否为fct或是gen。
class String {
public:
operator char *();
//...
};
在2.0 导入const member functions之前,conversion运算符不能够被overloaded,因为它们不使用参数。直到引进了const member functions,情况才有所变化。现在,像下面这样的声明就有可能了:
class String {
public:
// ok with Release 2.0
operator char *();
operator char *() const;
//...
};
也就是说,在2.0版本之前,以一个explicit cast来存取derived object总是安全(而且比较快速)的,像下面这样:
typedef type *ptype;
typedef fct *pfct;
simplify_conv_op(ptype pt)
{
// ok: conversion operator can only be fcts
pfct pf = pfct(pt);
//...
}
在const member functions引入之前,这份代码是正确的。但是在const member functions引进之后,不论程序注释还是程序代码就都不对了。程序之所以失败,是因为String class声明的改变。因为char *conversion运算符现在被内部视为一个gen而不是一个fct。
下面这样的转换形式:
pfct pf = pfct(pt);//type class to fct class
被称为downcast(向下转换),因为它有效的把一个base class 转换至继承框架的末端,变成其derived classes中的某一个。Downcast有潜在性的危险,因为它遏制了类型系统的作用,不正确的使用可能带来错误的解释(如果它是一个read操作)或腐蚀掉程序内存(如果它是一个write操作)。在我们的例子中,一个指向gen object的指针被不正确地转换为一个指向fct object地指针pf。所有后续对pf地使用都是不正确的。
Type-Safe Downcast(保证安全的向下转换操作)
C++被吹毛求疵的一点是,它缺乏一个保证安全的downcast。只有在“类型真的可以被适当转换”的情况下,你才能够执行downcast。一个type-safe downcast必须在执行期对指针有所查询,看它是否指向它所展示之object的真正类型。因此,欲支持type-safe downcast,在object空间和执行时间上都需要一些额外的负担:
- 需要额外的空间以存储类型信息(type information),通常是一个指针,指向某个类型信息节点。
- 需要额外的时间以决定执行期的类型(runtime type),因此正如其名所示,这需要在执行期才能决定。
C++的RTTI机制提供了一个安全的downcast设备,但只对那些展示“多态(也就是使用继承和动态绑定)”的类型有效。
Type-Safe Dynamic Cast(保证安全的动态转换)
dynamic_cast运算符可以在执行期决定真正的类型。如果downcast是安全的(也就是说,如果base type pointer指向一个derived class object),这个运算符会被传回被适当转换过的指针。如果downcast不是安全的,这个运算符会传回0。下面就是我们重写我们原本的cfront downcast:
typedef type *ptype;
typedef fct *pfct;
simplify_conv_op(ptype pt)
{
if(pfct pf = dynamic_cast<pfct>(pt)){
//表示pf转换成功了
//... process pf
}else{...}
}
什么是dynamic_cast的真正成本呢?pfct的一个类型描述器会被编译出来。由pt所指向的class object类型描述器必须在执行期通过vptr取得。下面就是可能的转换:
//取得pt的类型描述器
((type_info *)(pt->vptr[0]))->_type_descriptor;
ype_info是C++ Standard所定义的类型描述器的class名称,该class中放置着待索求的类型信息。Virtual table的第一个slot内含type_info object的地址;此type_info object与pt所指的class type有关。这两个类型描述器被交给一个runtime library函数,比较之后告诉我们是否吻合。
References并不是Pointers
程序执行中对一个class指针类型实施dynamic_cast运算符,会获得true或false:
- 如果传回真正的地址,则表示这一object的动态类型被确认了,一些与类型有关的操作现在可以施行于其上了。
- 如果传回0,则表示没有指向任何object,意味着应该以另一种逻辑施行于这个动态类型未确定的object身上。
dynamic_cast运算符也适用于reference身上。然而对于一个non-type-safe cast,其结果不会与施行于指针的情况相同。为什么?一个reference不可以像指针那样“把自己设置成0便代表了no object”。因此当dynamic_cast运算符施行于一个reference时候,不能提供像指针对等的那一组true/false。取代的是:
如果reference真正参考到适当的derived class,downcast会被执行而程序可以继续进行。
如果reference并不真正是某一种derived class,那么,由于不能传回0,因此抛出一个bad_cast exception。
下面是重新实现的simplify_conv_op函数,参数改为一个reference:
simplify_conv_op(const type rt)
{
try {
fct &rf = dynamic_cast<fct &>(rt);
}
catch(bad_cast){
//...mumble ...
}
}
Typeid运算符
使用typeid运算符,就有可以以一个reference达到相同的执行期代替路线(runtime ‘alternative pathway’):
simplify_conv_op(const type rt)
{
if(typeid(rt) == typeid(fct))
{
fct &rf = static_cast<fct &>(rt);
//...
}else{...}
}