首页 > 代码库 > RTTI和类型转换

RTTI和类型转换

RTTI
通过运行时类型识别(RTTI),程序能够使用基类的指针或引用来检索这些指针或引用所指对象的实际派生类型。通过下面两个操作符提供 RTTI:
1. typeid 操作符,返回指针或引用所指对象的实际类型
2. dynamic_cast 操作符,将基类类型的指针或引用安全地转换为派生类型的指针或引用。
这些操作符只为带有一个或多个虚函数的类返回动态类型信,对于其他类型,返回静态(即编译时)类型的信息。对于带虚函数的类,在运行时执行 RTTI 操作符,但对于其他类型,在编译
时计算 RTTI 操作符。
当具有基类的引用或指针,但需要执行不是基类组成部分的派生类操作的时候,需要动态的强制类型转换。通常,从基类指针获得派生类行为最好的方法是通过虚函数。当使用虚函数的时候,编译器自动根据对象的实际类型选择正确的函数。但是,在某些情况下,不可能使用虚函数。在这些情况下,RTTI 提供了可选的机制。然而,这种机制比使用虚函数更容易出错:程序员必须知道应该将对象强制转换为哪种类型,并且必须检查转换是否成功执行了。
使用动态强制类型转换要小心。只要有可能,定义和使用虚函数比直接接管类型管理好得多。

dynamic_cast
将基类类型对象的引用或指针转换为同一继承层次中其他类型的引用或指针。注:传入dynamic_cast的参数对应的类,必须带有virtual函数,否则,编译无法通过。dynamic_cast 涉及运行时类型检查。如果绑定到引用或指针的对象不是目标类型的对象,则 dynamic_cast 失败。如果转换到指针类型的 dynamic_cast 失败,则 dynamic_cast 的结果是 0 值;如果转换到引用类型的 dynamic_cast 失败,则抛出一个 bad_cast 类型的异常。

typeid 操作符
typeid 表达式形如:typeid(e);这里 e 是任意表达式或者是类型名。如果表达式的类型是类类型且该类包含一个或多个虚函数,则表达式的动态类型可能不同于它的静态编译时类型
typeid 操作符可以与任何类型的表达式一起使用。内置类型的表达式以及常量都可以用作 typeid 操作符的操作数。如果操作数不是类类型或者是没有虚函数的类,则 typeid 操作符指出操作数的静态类型;如果操作数是定义了至少一个虚函数的类类型,则在运行时计算类型。

typeid 操作符的结果是名为 type_info 的标准库类型的对象引用,要使用 type_info 类,必须包含库头文件typeinfo。
typeid 最常见的用途是比较两个表达式的类型,或者将表达式的类型与特定类型相比较:只有当 typeid 的操作数是带虚函数的类类型的对象的时候,才返回动态类型信息。测试指针(相对于指针指向的对象)返回指针的静态的、编译时类型。如果指针 p 的值是 0,那么,如果 p 的类型是带虚函数的类型,则typeid(*p) 抛出一个 bad_typeid 异常;如果 p 的类型没有定义任何虚函数,则结果与 p 的值是不相关的。正像计算表达式 sizeof一样,编译器不计算 *p,它使用 p 的静态类型,这并不要求 p 本身是有效指针。

实例
#include <iostream>
#include <typeinfo>

using namespace std;

class Base
{
     public:
          virtual void func(){cout << "I, Base." << endl;}
};

class Derived:public Base
{
     public:
          void dfunc(){cout << "I, Derived." << endl;}
};

class BadDerived
{
     public:
          virtual void func(){cout << "I, BadDerived." << endl;}
};
class T
{
     public:
          void dfunc(){cout << "I, T." << endl;}
};
int main()
{
     Base *baseptr = new Derived;
     BadDerived *badptr = new BadDerived;

     cout << endl << "*********Test 0************" << endl;
     if(T *deriveptr = dynamic_cast<T*>(baseptr))
     {
          cout << "success---->";
          deriveptr->dfunc();
     }
     else
     {
          cout << "failed----->";
          baseptr->func();
          cout << "DEBUG" << endl;
          cout << "typeid(T).name = " << typeid(T).name() << endl;
          cout << "typeid(Baseref).name = " << typeid(*baseptr).name() << endl;
     }
     cout << endl << "*********Test 1************" << endl;
     if(Derived *deriveptr = dynamic_cast<Derived*>(baseptr))
     {
          cout << "success---->";
          deriveptr->dfunc();
     }
     else
     {
          cout << "failed----->";
          baseptr->func();
          cout << "DEBUG" << endl;
          cout << "typeid(Derived).name = " << typeid(Derived).name() << endl;
          cout << "typeid(Baseref).name = " << typeid(*baseptr).name() << endl;
     }
     cout << endl << "*********Test 2************" << endl;
     if(Derived *deriveptr = dynamic_cast<Derived*>(badptr))
     {
          cout << "success---->";
          deriveptr->dfunc();
     }
     else                            
     {                               
          cout << "failed----->";
          badptr->func();
          cout << "DEBUG" << endl;
          cout << "typeid(Derived).name = " << typeid(Derived).name() << endl;
          cout << "typeid(Baseref).name = " << typeid(*badptr).name() << endl;
     }

     Derived deriveobj;
     Base &baseref = deriveobj;
     BadDerived badobj;
     BadDerived &badref = badobj;

     cout << endl << "*********Test 3************" << endl;
     try
     {
          Derived &deriveref = dynamic_cast<Derived&>(baseref);
          cout << "success---->";
          deriveref.dfunc();
     }
     catch(bad_cast)
     {
          cout << "failed----->";
          baseref.func();
          cout << "DEBUG" << endl;
          cout << "typeid(Derived).name = " << typeid(Derived).name() << endl;
          cout << "typeid(Baseref).name = " << typeid(baseref).name() << endl;
     }
     cout << endl << "*********Test 4************" << endl;
     try
     {
          Derived &deriveref = dynamic_cast<Derived&>(badref);
          cout << "success---->";
          deriveref.dfunc();
     }
     catch(bad_cast)
     {
          cout << "failed----->";
          baseref.func();
          cout << "DEBUG" << endl;
          cout << "typeid(Derived).name = " << typeid(Derived).name() << endl;
          cout << "typeid(Baseref).name = " << typeid(badref).name() << endl;
     }
     cout << endl << "*********Test 5************" << endl;
     if(typeid(Derived) == typeid(baseref))
     {
          Derived &deriveref = dynamic_cast<Derived&>(baseref);
          cout << "success---->";
          deriveref.dfunc();
     }
     else
     {
          cout << "failed----->";
          baseref.func();
          cout << "DEBUG" << endl;
          cout << "typeid(Derived).name = " << typeid(Derived).name() << endl;
          cout << "typeid(Baseref).name = " << typeid(baseref).name() << endl;
     }
     cout << endl << "*********Test 6************" << endl;
     if(typeid(Derived) == typeid(badref))
     {
          Derived &deriveref = dynamic_cast<Derived&>(badref);
          cout << "success---->";
          deriveref.dfunc();
     }
     else
     {
          cout << "failed----->";
          badref.func();
          cout << "DEBUG" << endl;
          cout << "typeid(Derived).name = " << typeid(Derived).name() << endl;
          cout << "typeid(Baseref).name = " << typeid(badref).name() << endl;
     }
}

执行结果
*********Test 0************
failed----->I, Base.
DEBUG
typeid(T).name = 1T
typeid(Baseref).name = 7Derived

*********Test 1************
success---->I, Derived.

*********Test 2************
failed----->I, BadDerived.
DEBUG
typeid(Derived).name = 7Derived
typeid(Baseref).name = 10BadDerived

*********Test 3************
success---->I, Derived.

*********Test 4************
failed----->I, Base.
DEBUG
typeid(Derived).name = 7Derived
typeid(Baseref).name = 10BadDerived

*********Test 5************
success---->I, Derived.

*********Test 6************
failed----->I, BadDerived.
DEBUG
typeid(Derived).name = 7Derived
typeid(Baseref).name = 10BadDerived

类型转换

C风格转换是“万能的转换”,但需要程序员把握转换的安全性,编译器无能为力。

类型转换的不安全来源于两个方面:其一是类型的窄化转化,会导致数据位数的丢失;其二是在类继承链中,将父类对象的地址(指针)强制转化成子类的地址(指针),这就是所谓的下行转换,“下”表示沿着继承链向下走(向子类的方向走)。类似地,上行转换的“上”表示沿继承链向上走(向父类的方向走);上行转换一般是安全的,下行转换很可能是不安全的;因为子类中包含父类,所以上行转换(只能调用父类的方法,引用父类的成员变量)一般是安全的。但父类中却没有子类的任何信息,而下行转换会调用到子类的方法、引用子类的成员变量,这些父类都没有,所以很容易“指鹿为马”或者干脆指向不存在的内存空间。

static_cast <new_type> (expression) 静态转换

static_cast最接近于C风格转换,但在无关类指针转换时,编译器会报错,提升了安全性。

dynamic_cast <new_type> (expression) 动态转换

动态转换确保类指针的转换是合适完整的,它有两个重要的约束条件,其一是要求new_type为指针或引用,其二是下行转换时要求基类是多态的(基类中包含至少一个虚函数),如果发现下行转换不安全,dynamic_cast返回一个null指针。当待转换指针是void*或者转换目标指针是void*时,dynamic_cast总是认为是安全的。

reinterpret_cast <new_type> (expression) 重解释转换

这个转换是最“不安全”的,两个没有任何关系的类指针之间转换都可以用这个转换实现。reinterpret_cast可以把整型数转换成地址(指针),这种转换在系统底层的操作,有极强的平台依赖性,移植性不好;它同样要求new_type是指针或引用

const_cast <new_type> (expression) 常量向非常量转换

const_cast可以将常量转成非常量,但不会破坏原常量的const属性,只是返回一个去掉const的变量。从char *cc = const_cast<char *>(c)可以看出了这个转换的作用了,但切记,这个转换并不转换原常量本身,即c还是常量,只是它返回的结果cc是非常量了。




RTTI和类型转换