RTTI简介

代码编译运行平台:VS2012+Debug+Win32


RTTI是Runtime Type Identification的缩写,是“运行时类型识别”的意思。面向对象的编程语言,象C++,Java,Delphi都提供了对RTTI的支持。 本文将简略介绍 RTTI 的一些背景知识、描述 RTTI 的概念,并通过具体例子和代码介绍什么时候使用以及如何使用 RTTI。本文还将详细描述两个重要的 RTTI 运算符的使用方法,它们是 typeid 和dynamic_cast。


1. typeid的用法

1.1type_info类

typeid 的结果是 const type_info&。所以下面先对type_info类作下介绍:

class type_info {
public:
    virtual ~type_info();
    size_t hash_code() const
    _CRTIMP_PURE bool operator==(const type_info& rhs) const;
    _CRTIMP_PURE bool operator!=(const type_info& rhs) const;
    _CRTIMP_PURE int before(const type_info& rhs) const;
    _CRTIMP_PURE const char* name() const;
    _CRTIMP_PURE const char* raw_name() const;
};

我们不能直接实例化 type_info 类的对象,因为该类只有一个私有复制构造函数。 构造(临时)type_info 对象的唯一方式是使用 typeid 运算符。 由于赋值运算符也是私有的,因此不能复制或分配类 type_info 的对象。

1.2typeid应用实例

1.2.1typeid静态的类型判断

typeid可以静态地确定操作数的类型,也可以动态地确定操作数的类型,这取决于操作数本身是否拥有虚函数。当typeid的操作数是一个基本类型的变量,或者是一个不带虚函数的对象时,typeid的运行结果是在编译阶段决定的。所以是一种静态的类型判断。见下面的例子。

#include <iostream>
using namespace std;


class A{
    int i;
public:
    void show(){
        cout<<"In class A"<<endl;
    }
};


class B{
    int i;
public:
    void show(){
        cout<<"In class B"<<endl;
    }
};

template < typename T>void func(T& a){
    if(typeid(A)==typeid(T))
        cout<<"is A"<<endl;
    if(typeid(B)==typeid(T))
        cout<<"is B"<<endl;
}

int main(){
    if(typeid(B)==typeid(A))
        cout<<"A equal to B"<<endl;
    else
        cout<<"A not equal to B"<<endl;

    A a;
    B b;
    func<B>(b);
    func<B>(b);
    cout<<typeid(B).name()<<endl;
    getchar();
}

程序输出结果: A not equal to B is B is B class B

在上面的程序中,函数模板func()被实例化为class A和class B时,typeid(T)是在编译阶段静态确定的。在函数模板内部,可以通过typeid操作决定在模板参数被实例化为不同数据类型的时要采取不同的行动。

1.2.2typeid动态类型判断

typeid更多的时候是在运行时用来动态地确定指针或引用所指向对象的类型,这时要求typeid所操作的对象一定要拥有虚函数。见下面的程序。

#include <iostream>
using namespace std;

class A{
    virtual void func(){}
};

class B:public A{};

void reportA(A* pa){
    if(typeid(*pa)==typeid(A))
        cout<<"Type of *pb is A"<<endl;
    else if(typeid(*pa)==typeid(B))
        cout<<"Type of *pb is B"<<endl;
}

void reportB(B* pb){
    if(typeid(*pb)==typeid(A))
        cout<<"Type of *pb is A"<<endl;
    else if(typeid(*pb)==typeid(B))
        cout<<"Type of *pb is B"<<endl;
}

int main(){
    A a,*pa;
    B b,*pb;
    pa=&a;
    reportA(pa);
    pa=&b;
    reportA(pa);
    pb=static_cast<B*>(&a);
    reportB(pb);
    pb=&b;
    reportB(pb);
}

程序输出结果: Type of *pb is A Type of *pb is B Type of *pb is A Type of *pb is B

从上面的运行结果可以看出,使用typeid确实能够在程序运行期间动态地判断指针所指对象的实际类型。使用引用可以到达同样的效果,因为引用的底层实现就是指针。

要注意的是, (1)如果在Class A的定义中,将函数func()定义为普通函数(即将前面的virtual关键字去掉),那么typeid(*pa)的结果永远是typeid(A),而typeid(*pb)的结果永远是typeid(B)。也就是说,由于pa和pb所指向的对象中没有虚函数,该对象就没有虚函数表存放运行时信息,typeid实际就变成了一种静态运算符。

(2)C++中的一切“动态”机制,包括虚函数、RTTI等,都必须通过指针或引用来实现。换句话说,指针所指的对象或引用所绑定的对象,在运行阶段可能与声明指针或引用时的类型不一致。如果不使用指针或引用,而是直接通过对象名访问对象,那么及时对象拥有动态信息(虚函数表),对象的动态信息与静态申明对象时的信息必然一致,就没有必要访问虚函数表;而如果对象不拥有虚函数,就没有虚函数表存放动态信息,也就无法在运行时动态判断指针所指向对象(或引用所绑定对象)的实际类型。

2.dynamic_cast的用法

详见我的另一篇blogC/C++数据类型的转换之终极无惑


参考文献

[1]百度百科.RTTI [2]陈刚.C++高级进阶教程[M].武汉:武汉大学出版社,2008[8.6(P288-P290)] [3]https://msdn.microsoft.com/zh-cn/library/fyf39xec.aspx [4]http://blog.csdn.net/mannhello/article/details/5217954

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java帮帮-微信公众号-技术文章全总结

【选择题】Java基础测试五(15道)

【选择题】Java基础测试五(15道) 56.下列哪个类的声明是正确的?( D ) A.abstract final class HI{} (final代...

3558
来自专栏C/C++基础

如何禁止函数的传值调用

按照参数形式的不同,C++应该有三种函数调用方式:传值调用、引用调用和指针调用。对于基本数据类型的变量作为实参进行参数传递时,采用传值调用与引用调用和指针调用的...

711
来自专栏carven

浅谈闭包

闭包 – closure, 应该可以说是javascript的一个难点吧, 其实说难也不难, 只是因为没有真正一个权威的人/书去给他一个真正的定义。 不过,学编...

940
来自专栏CVer

排序算法 | 冒泡排序(含C++/Python代码实现)

排序算法,就是如何使得记录按照要求排列的方法。排序算法在很多领域得到相当地重视,尤其是在大量数据的处理方面。排序算法有很多,本文将介绍最经典的排序算法:冒泡排序...

1332
来自专栏静晴轩

浅谈java中extends与implements的区别

  Extends可以理解为全盘继承了父类的功能。implements可以理解为为这个类附加一些额外的功能;interface定义一些方法,并没有实现,需要im...

3708
来自专栏数据科学与人工智能

【Python环境】12道 Python面试题总结

1、Python是如何进行内存管理的? Python的内存管理主要有三种机制:引用计数机制、垃圾回收机制和内存池机制。 a. 引用计数 当给一个对象分配一个新名...

2545
来自专栏牛肉圆粉不加葱

[7] - trait

这是我以前在知乎上看到关于类继承作用的回答,虽不完全正确,却十分明确的表达出了好的代码应避免类继承而尽量使用类组合。Scala 显然也非常赞同这一点,以至于有了...

1072
来自专栏Redis

Redis类型之sorted sets类型

Redis类型之sorted sets类型

2024
来自专栏landv

Java对象和类

2124
来自专栏PHP在线

PHP中的函数

函数调用 function sum($x,$y) //形参:在声明函数时声明的参数 { // $x = 1; //如果在函数内对参数赋值,则会覆盖实参。...

3745

扫码关注云+社区

领取腾讯云代金券