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是非常量了。




  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值