RTTI

  RTTI是运行阶段类型识别(Runtime Type Identification)的简称。RTTI旨在为程序运行阶段确定对象的类型提供一种标准方式。

RTTI的用途
  假设有一个类层次结构,其中的类都是从同一个基类派生而来,则可以让基类的指针指向其中任何一个类的对象。这样便可以调用这样的函数:在处理一些信息后,选择一个类,并创建这种类型的对象,然后返回它的地址,而该地址可以被赋给基类指针。但是如何知道指针指向的是哪种对象呢?
  在这个问题之前,先要考虑为何要知道类型。
1.可能希望调用类方法的正确版本,在这种情况下,只要该函数是类层次结构中所有成员都拥有的虚函数,则并不真正需要知道对象的类型。
2.派生对象包含不是继承而来的方法,这种情况下,只有某些类型的对象可以使用该方法。
3.处于调试目的,想跟踪生成的对象的类型。
对于后两种情况,RTTI提供解决方案。

RTTI的工作原理
C++有3个支持RTTI的元素:
1.如果可能的话,dynamic_cast运算符将使用一个指向基类的指针来生成一个指向派生类的指针;否则,该运算符返回0——空指针。
2.typeid运算符返回一个指出对象的类型的值。
3.type_info结构存储了有关特定类型的信息。
注意:只能将RTTI用于包含虚函数的类层次结构,原因在于只有对于这种类层次结构,才应该将派生对象的地址赋给基类指针。

dynamic_cast运算符
  dynamic_cast运算符不能知道“指针指向的是哪类对象”,只能知道“是否可以安全的将对象的地址赋给特定类型的指针”,它用于将派生类指针转换为基类指针,其主要用途是确保可以安全的调用虚函数。

class Grand {};
class Superb : public Grand{};
class Magnificent : public Superb{};

Grand * pg = new Grand;
Grand * ps = new Superb;
Grand * pm = new Magnificent;

Magnificent * p1 = (Magnificent *) pm;//安全
Magnificent * p2 = (Magnificent *) ps;//不安全
Superb * p3 = (Magnificent *) pm;//安全

Superb * pm = dynamic_cast<Superb *>(pg);//含义是指针pg的类型是否可被安全的转换为Superb *,如果可以将返回对象的地址,否则返回一个空指针。
通常,如果指向的对象(*pt)的类型为Type或者是从Type直接或间接派生而来的类型,则下面的表达式将指针pt转换为Type类型的指针。

dynamic_cast<Type *>(pt)

即使编译器支持RTTI,在默认情况下,它也可能关闭该特性,如果该特性被关闭,程序可能仍能够通过编译,但将出现运行阶段错误。

#include <iostream>
#include <cstdlib>
#include <ctime>

using std::cout;
class Grand
{
    int hold;
public:
    Grand(int h = 0) : hold(h) {}
    virtual void Speak() const {cout << "I am a grand class!\n";}
    virtual int Value() const {return hold;}
};

class Superb : public Grand
{
public:
    Superb(int h = 0) : Grand(h) {}
    void Speak() const {cout << "I am a superb class!!\n";}
    virtual void Say() const 
        {cout << "I hold the superb value of " << Value() << "!\n";}
};

class Magnificent : public Superb
{
    char ch;
public:
    Magnificent(int h = 0, char c = 'A') : Superb(h), ch(c) {}
    void Speak() const {cout << "I am a mangnificent class!!!\n";}
    void Say() const {cout << "I hold the character" << ch <<
                        "and the integer" << Value() << "!\n";}

};

Grand * GetOne();

int main(int argc, char ** argv)
{
    std::srand(std::time(0));
    Grand * pg;
    Superb * ps;
    for (int i = 0; i < 5; i++)
    {
        cout << "i = " << i + 1 << ":   ";
        pg = GetOne();
        pg->Speak();
        if (ps = dynamic_cast<Superb *>(pg))//判断pg是否可以安全转换为Superb *
        {
            ps->Say();
        }
    }

    return 0;
}

Grand * GetOne()
{
    Grand * p;
    switch (std::rand() % 3)
    {
        case 0: p = new Grand(std::rand() % 100);
                break;
        case 1: p = new Superb(std::rand() % 100);
                break;
        case 2: p = new Magnificent(std::rand() % 100, 'A' + std::rand() % 26);
                break;
    }
    return p;
}

在这里插入图片描述
从上面的输出来看,应尽可能的使用虚函数。而只在必要的时候使用RTTI。也可以将dynamic_cast用于引用,

Superb & rs = dynamic_cast<Superb &>(rg);

其用法稍微有点不同:没有与空指针对应的引用值,因此无法使用特殊的引用值来指示失败。当请求不当时,dynamic_cast将引发类型为bad_cast的异常。

2.typeid运算符和type_info类
typeid运算符使得能够确定两个对象是否为同种类型。它与sizeof有些相像,可以接受两种参数:
• 类名;
• 结果为对象的表达式;
typeid运算符返回一个对type_info对象的引用,type_info是在头文件typeinfo中定义的一个类。

typeid(Magnificent) == typeid(*pg);
  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值