C/C++编程:RTTI机制

1060 篇文章 295 订阅
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对象,而不是程序一开头就生成

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值