class node{...};
class type : public node{ ... };
class fct : public type{...};
class gen : public type{...};
typedef type *ptype;
typedef fct *pfct;
type-safe downcast
downcast(向下转型):把基类转换为派生类
在C++2.0之前,可以使用explicit cast来将
simplify_conv_op(ptype pt){
pfct = pfct(pt);
}
但是这可能带来错误,比如如果pt实际上指向gen而不是pct时。
C++缺少一个保证安全的向下转型操作。只有在"类型真的可以被适当转型"的情况下,才能执行downcast。一个type-safe downcast必须在执行期对指针有所查询,看看它是否指向它所展现的object的真正类型。因此,欲支持type-safe downcast,在object空间和执行时间上都需要一些额外负担:
- 需要额外的空间以存储类型信息,通常是一个指针,指向某个类型的信息节点
- 需要额外的时间以决定执行期的类型。
但是:
- 程序员大量使用多态,并因而需要正统而合法的大量downcast操作
- 程序员使用内建数据类型以及非多态设备,因而不受各种额外负担所带来的报应
C++的RTTI机制提供了一个安全的downcast,但只对那些展现"多态"(即使用继承和动态绑定)的类型有效。我们如何分辨这些?编译器能否光看类的定义决定这个类用以表现一个独立的ADT或者是一个支持多态的可继承子类型?
RTTI机制的策略是使用仅由声明一个或者多个的虚函数来区别类类型。
- 在C++中,一个具备多态的类,正是内含着继承而来(或者直接声明)的虚函数
- 所有多态类的对象都维护了一个指针(vptr),指向虚函数表。只要我们把与该类相关的RTTI对象地址放进虚函数表中(通常放在第一个slot)。这个指针只需要设定一次,它是被编译器静态设定,而不是在执行期由类析构函数设定
type-safe dynamic cast
dynamic_cast
运算符可以在执行期决定真正的类型。
- 如果downcast是安全的(也就是说,如果base type pointer指向一个derived class object),这个运算符会传回被适当转型过的指针。
- 如果downcast不是安全的,这个运算符会传回0
simplify_conv_op(ptype pt){
if(pfct pf = dynamic_cast<pfct>(pt)){
}
}
那dynamic_cast有什么成本吗?pfct的一个类型描述器会被编译器产生出来。由pt指向的类对象的类型描述器必须在执行期通过vptr取得:
// 取得pt的类型描述器
((type_info*)(pt->vptr[0]))->_type_descriptor;
type_info是C++标准所定义的类型描述器的类名称,该类中放置着所索求的类型信息。虚函数表的第一个slot内含type_info object的地址:这个type_info 对象于pt所指的class type有关。这两个类型描述器被交给一个运行库函数,比较之后告诉我们是否吻合。很显然比static_cast昂贵得多,但是安全得多。
引用与动态类型转换
程序执行中对一个类指针类型施以dynamic_cast运算符,获得true或者false
- 如果传回真正的地址,表示这个对象的动态类型被确认了
- 如果传回0,表示没有指向任何对象
dynamic_cast也可以用于引用。然后对一个non-type-safe-cast,其结果不会与施行于指针的情况相同。为什么?应该引用不可以像指针一样"把自己设为0以表示on object",如果把一个引用设置为0,会引起一个临时性对象被产生(拥有被参考到的类型),该临时对象的初值为0,这个引用然后被设定为该临时对象的一个别名。也就是说,当dynamic_cast被用于引用时:
- 如果引用真正参考到适当的派生类,downcast会被执行而程序可以继承进行。
- 如果引用并不真正是某一种派生类,那么,由于不能传回0,就抛出一个bas_cast exception。
simplify_conv_op(const type & rt){
try{
fct &rf = dynamic_cast<fct&>(rt);
}catch(bad_cast){
}
}
typeid运算符
使用typeid
运算符,就有可能以一个引用达到相同的执行期替代路线:
typedef type *ptype;
typedef fct *pfct;
simplify_conv_op(const type & rt){
if(typeid(rt) == typeid(fct)){
fct &rf = static_cast<fct&>(rt);
}
}
typeid运算符传回一个const reference,类型为type_info,对于==
:
bool type_info:: operator==(const type_info&) const;
如果两个type_info objects相等,这个==
返回type。
对于type_info:
class type_info{
public:
virtual ~type_info();
bool operator==(const type_info&)const;
bool operator!=(const type_info&)const;
bool before(const type_info&) const;
const char *name() const; // 传回class的原始名称
private:
type_info(connst type_info&);
type_info& operator=(const type_info&);
// ...
};
编译器必须提供的最小量信息是类的真是名称、以及在type_info对象之间的排序算法(这就是before()函数的目的)、以及某些形式的描述器,用来表现explicit class type和这个class的任何subtypes。
RTTI除了用于多态类,也适用于内建类型:
int *ptr;
if(typeid(ptr) == typeid(int *))
typeid(ptr)
会传回一个const type_info&,这与先前的多态类型的差异在于,这时候的type_info对象是静态取得,而不是执行期取得。一般的实现策略是在需要的时候才产生type_info对象,而不是程序一开头就生成