Type-Safe Downcast(保证安全的向下转换操作)
C++被吹毛求疵的一点,它缺乏一个保证安全的downcast(向下转换操作)。只有在“类型真的可以被适当转换”的情况下,你才能够执行downcast。一个type-safe downcast必须在执行期对指针所有查询,看看它是否指向它所展现之object真正的类型。因此,欲支持type-safe downcast,在object空间和执行时间上都需要一些额外的负担:
- 额外的空间存储类型信息(type information),通常是一个指针,指向某个类型信息节点。
- 额外的时间以决定执行期的类型(runtime tpye),这需要在执行期才能决定。
这种机制对于下面的C结构,会如何影响:
char* winnie_tbl[] = {"rumbly in my tummy","on,bother"};
会导致空间和效率上的不良后果:
- 程序员大量使用多态,并因此需要大量的downcast。
- 程序员使用内建数据类型以及非多态,就不会需要额外的负担所带来的不良结果。
C++的RTTI提供了一个安全的downcast,但只是针对那些展现(多态)的类型有效。分辨一个class的定义就是觉得这个class用以表现一个独立的ADT还是一个支持多态的可继承子类型(subtype)。策略之一就是导入一个新的关键词,优点可以清楚的识别出新特质的类型,缺点就是必须翻新久程序。
另一策略就是通过声明一个或多个virtual function来区别class的声明。其优点是透明化的将旧程序转换过来,只要重新编译好;缺点这是可能会将一个非必要的virtual function强迫导入继承体系的base class身上。目前C++的RTTI就是这个策略。
从编译器角度来看,这个策略还是有其他优点:就是大量降低了额外的负担。所有动态类的objects都维护了一个指针(vptr),指向vtbl。只要我们把该class的RTTI object地址放进virtual table,那么额外的负担就降低为:每一个class object 只要多花费一个指针。这指针只被设定一次,它是被编译器静态编译的,而非执行期由class constructor设定(vptr才是这么设定的)。
Type-Safe Dynamic Cast(保证安全的动态转换)
dynamic_cast运算符可以在执行期决定真正的类型。如果downcast是安全的,这个运算符会传回被被转换过的指针,如果downcast不安全,会传回0。
动态转换的成本:对于一个基类指针转化成子类的指针,必须通过class object类型描述符在执行期通过vptr取得的:
((type_info*)(pt->vptr[0]))->_type_descriptor;
type_info是C++ Standard所定义的类型描述器的class名称,该class 中放着待索求的类型信息。virtual table的第一个slot内含type_info object的地址;此type_info object与pt所指的class type有关。这两个类型描述器被交给一个runtime library函数,比较之后告诉我们是否吻合。这个比static cast昂贵的多,但却安全得多。
References并不是Pointers
dynamic_cast运算符也使用于reference身上。但是引用不能为0。对于向下转换不成功,指针传回0;引用是抛出异常。
Typeid运算符
使用typeid运算符,就有可能以一个reference达到相同的执行期替代路线:
typeid运算符传回一个const reference,类型为type_info。
对于typeid的测试等的运算符,是一个被overload的函数(typeid(a) == typeid(b)):
bool type_info::operator == (const type_info&) const;
type_info object定义如下:
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;
private:
type_info(const type_info&);
type_info& operator == (const type_info&);
};
虽然RTTI提供了type_info对于exception handing的支持来说必要的,但对于exception handing的完全支持而言,还不够。如果再加上额外的一些type_info derived classes,就可以在exception发生时提供关于指针、函数、类等等的更详细的信息。
虽然RTTI适合于多态类,事实上type_info objects也适合于内建类型,以及fei多态的使用者的自定义类型。这对exception handing的支持是有必要的。例如:
int ex_errno;
...
throw ex_errno;
//int类型也有自己的type_info object :
int* ptr;
...
if(typeid(ptr) == typeid(int*))
...
//在程序中使用typeid(expression)
int ival;
...
typeid(ival)...;
//或使用typeid(type);
typeid(double)...;
上述使用typeid会传回一个const type_info&,这是静态取得的,非执行期。一般实现策略是在需要时才产生type_info object,而非程序一开头就产生。