运行时类型识别的功能由两个运算实现:
- typeid运算符,用于返回表达式的类型
- dynamic_cast,用于将基类的指针或引用安全的转换为派生类的指针或引用
将这两个运算符用于某种类型的指针或引用,并且该类型含有虚函数时,运算符将使用指针或引用所绑定对象的动态类型。
这两个运算符特别适用于以下情况:
我们想使用基类对象的指针或引用执行某个派生类操作并且该操作不是虚函数。
1.typeid运算符
typeid表达式的形式是:
typeid(e);//e可以是任意表达式或类型的名字
typeid操作的结果是一个常量对象的引用,该对象的引用类型是标准类型type_info或者type_info的公有派生类型。
void f(const Base&b)
{
}
int main()
{
int i[10];
void *p=0;
const Base *const b=new Derived();
cout << typeid(i).name() << endl;//int [10]
cout << typeid(p).name() << endl;//void *
cout << typeid(f).name() << endl;//void _cdecl(class Base const &)
cout << typeid(*b).name() << endl;//class Derived
cout << typeid(b).name() << endl;//class Derived*
}
当typeid作用于数组或函数时,并不会执行向指针的标准类型转换。
当运算对象不属于类类型或者是一个不包含任何虚函数的类时,typeid运算符指示的是运算的静态类型。
当运算对象含有虚函数时,typeid的结果直到运行时才会求得。
type_info类
type_info类必须定义在typeinfo头文件中,至少提供以下操作
t1==t2
t1 != t2
t.name()
t1.before(t2)
//type_info精简源代码:
class type_info
{
public:
virtual ~type_info();
bool operator==(const type_info& _Rhs) const; // 用于比较两个对象的类型是否相等
bool operator!=(const type_info& _Rhs) const; // 用于比较两个对象的类型是否不相等
bool before(const type_info& _Rhs) const;
// 返回对象的类型名字,这个函数用的很多
const char* name(__type_info_node* __ptype_info_node = &__type_info_root_node) const;
const char* raw_name() const;
private:
void *_M_data;
char _M_d_name[1];
type_info(const type_info& _Rhs);
type_info& operator=(const type_info& _Rhs);
static const char * _Name_base(const type_info *,__type_info_node* __ptype_info_node);
static void _Type_info_dtor(type_info *);
};
type_info类没有默认构造函数,而且复制构造函数和拷贝构造函数、移动构造函数都是私有的,因此,我们无法定义或拷贝type_info对象,也不能为type_info类型的对象赋值。
创建type_info对象的唯一途径是使用typeid运算符。
typeid函数是type_info类的一个引用对象,可以访问type_info类的成员。但因为不能创建type_info类的对象,而typeid又必须反回一个类型为type_info类型的对象的引用,所以怎样在typeid函数中创建一个type_info类的对象以便让函数反回type_info类对象的引用就成了问题。这可能是把typid函数声明为了type_info类的友元函数来实现的,默认构造函数并不能阻止该类的友元函数创建该类的对象。所以typeid函数如果是友元的话就可以访问type_info类的私有成员,从而可以创建type_info类的对象,从而可以创建返回类型为type_info类的引用。
RTTI的使用
比如当我们想为具有继承关系的类实现相等运算符时。
class Base
{
friend bool operator==(const Base&, const Base&);
protected:
virtual bool equal(const Base& )const;
pbulic:
//公有成员
};
class Derived
{
protected:
virtual bool equal(const Base&)const;
};
bool opeartor==(const Base& lhs, const Base& rhs)
{
return typeid(lhs)==typeid(rhs) && lhs.equal(rhs);
}
bool Derived::equal(const Base& rhs)
{
auto r=dynamic_cast<const Derived &>(rhs);
//执行比较操作;
}
bool Base::equal(const Base& rhs)
{
//执行比较操作;
}
2.dynamic_cast
使用形式:
dynamic_cast<type*>(e)
dynamic_cast<type&>(e)
dynamic_cast<type&&>(e)
其中,type必须是一个类类型,并且通常情况下该类含有虚函数。
e的类型必须符合以下条件之一:
- e的类型是目标type的公有派生
- e的类型是目标type的公有基类
- e的类型是目标type类型
如果符合,则类型转换成功,否则失败。
目标类型type是指针类型并失败时,则结果为0;目标类型是引用类型并失败,抛出一个bad_cast异常。
dynamic_cast主要用于在多态的时候,它允许在运行时刻进行类型转换,从而使程序能够在一个类层次结构中安全地转换类型,把基类指针(引用)转换为派生类指针(引用)。当类中存在虚函数时,编译器就会在类的成员变量中添加一个指向虚函数表的vptr指针,每一个class所关联的type_info object也经由virtual table被指出来,通常这个type_info object放在表格的第一个slot。当我们进行dynamic_cast时,编译器会帮我们进行语法检查。如果指针的静态类型和目标类型相同,那么就什么事情都不做;否则,首先对指针进行调整,使得它指向vftable,并将其和调整之后的指针、调整的偏移量、静态类型以及目标类型传递给内部函数。其中最后一个参数指明转换的是指针还是引用。两者唯一的区别是,如果转换失败,前者返回NULL,后者抛出bad_cast异常。
这篇博客写的不错 http://blog.csdn.net/ljianhui/article/details/46487951