C++中的RTTI机制

简介: C++中的RTTI机制目录C中的RTTI机制目录参考资料什么是RTTI机制为什么需要RTTI机制C中如何实现RTTI机制 注意不要盲目相信以下内容! 不要盲目相信以下内容! 不要盲目相信以下内容! (重要的事情说三遍),虽然以下内容也经过了我的验证,但是我的验证可能有错误的地方,欢迎大家留言告知。
+关注继续查看

C++中的RTTI机制

目录


注意不要盲目相信以下内容! 不要盲目相信以下内容! 不要盲目相信以下内容! (重要的事情说三遍),虽然以下内容也经过了我的验证,但是我的验证可能有错误的地方,欢迎大家留言告知。希望这篇文章成为你深入探索相关领域的引子启发,而不是标准答案


参考资料

百度百科RTTI
C++中的RTTI机制详解
RTTI
C++中的RTTI(转)
[转]c++中RTTI的观念和使用

什么是RTTI机制?

RTTI是“Runtime Type Information”的缩写,意思是:运行时类型信息。它提供了运行时确定对象类型的方法。

RTTI通过运行时类型信息程序能够使用基类的指针或引用来检查这些指针或引用所指的对象的实际派生类型。
听起来很像包含虚函数的多态机制,这里和虚函数又有哪些不同呢?

为什么需要RTTI机制?

数组是十分常用的数据结构,而对经常使用C++的同学来说指针也是逃不开的拦路虎。相信在很多课程或书本上大家都看过说指针在内存中存储的是一个地址,而数组名也是一个地址。

和很多其他语言一样,C++是一种静态类型语言。其数据类型是在编译期就确定的,不能在运行时更改。然而由于面向对象程序设计中多态性的要求,C++中的指针或引用(Reference)本身的类型,可能与它实际代表(指向或引用)的类型并不一致。

有时我们需要将一个多态指针转换为其实际指向对象的类型,就需要知道运行时的类型信息,这就产生了运行时类型识别的要求。

在C++中存在虚函数,也就存在了多态性,对于多态性的对象,在程序编译时可能会出现无法确定对象的类型的情况。

当类中含有虚函数时,其基类的指针就可以指向任何派生类的对象,这时就有可能不知道基类指针到底指向的是哪个对象的情况,类型的确定要在运行时利用运行时类型标识做出。为了获得一个对象的类型可以使用typeid函数,该函数反回一个对type_info类对象的引用,要使用typeid必须使用头文件typeinfo。

C++中如何实现RTTI机制?

C++提供了两个关键字typeid和dynamic_cast和一个type_info类来支持RTTI:

  dynamic_cast操作符:它允许在运行时刻进行类型转换,从而使程序能够在一个类层次结构安全地转换类型。dynamic_cast提供了两种转换方式,把基类指针转换成派生类指针,或者把指向基类的左值转换成派生类的引用。
  typeid操作符:它指出指针或引用指向的对象的实际派生类型。主要作用就是让用户知道当前的变量是什么类型的,比如以下代码:

#include <iostream>
#include <typeinfo>
using namespace std;

int main()
{
     short s = 2;
     unsigned ui = 10;
     int i = 10;
     char ch = 'a';
     wchar_t wch = L'b';
     float f = 1.0f;
     double d = 2;

     cout<<typeid(s).name()<<endl; // short
     cout<<typeid(ui).name()<<endl; // unsigned int
     cout<<typeid(i).name()<<endl; // int
     cout<<typeid(ch).name()<<endl; // char
     cout<<typeid(wch).name()<<endl; // wchar_t
     cout<<typeid(f).name()<<endl; // float
     cout<<typeid(d).name()<<endl; // double

     return 0;
}

对于C++支持的内建类型,typeid能完全支持,我们通过调用typeid函数,我们就能知道变量的信息。对于我们自定义的结构体,类呢?
  

#include <iostream>
#include <typeinfo>
using namespace std;

class A
{
public:
     void Print() { cout<<"This is class A."<<endl; }
};

class B : public A
{
public:
     void Print() { cout<<"This is class B."<<endl; }
};

struct C
{
     void Print() { cout<<"This is struct C."<<endl; }
};

int main()
{
     A *pA1 = new A();
     A a2;

     cout<<typeid(pA1).name()<<endl; // class A *
     cout<<typeid(a2).name()<<endl; // class A

     B *pB1 = new B();
     cout<<typeid(pB1).name()<<endl; // class B *

     C *pC1 = new C();
     C c2;

     cout<<typeid(pC1).name()<<endl; // struct C *
     cout<<typeid(c2).name()<<endl; // struct C

     return 0;
}

是的,对于我们自定义的结构体和类,tpyeid都能支持。
在上面的代码中,在调用完typeid之后,都会接着调用name()函数,可以看出typeid函数返回的是一个结构体或者类,然后,再调用这个返回的结构体或类的name成员函数。

  typeid的返回是type_info类型
  
  type_info类:这个类的确切定义是与编译器实现相关的,因为type_info类的复制构造函数和赋值运算符都是私有的,所以不允许用户自已创建type_info的对象,比如type_info A;错误,没有默认的构造函数。唯一要使用type_info类的方法就是使用typeid函数。

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 A;这样是错误的。那么typeid函数是如何返回一个type_info类的对象的引用的呢?

dynamic_cast操作符,将基类类型的指针或引用安全地转换为派生类型的指针或引用。这里就要对于dynamic_cast的内幕一探究竟。首先来看一段代码:

#include <iostream>
#include <typeinfo>
using namespace std;

class A
{
public:
     virtual void Print() { cout<<"This is class A."<<endl; }
};

class B
{
public:
     virtual void Print() { cout<<"This is class B."<<endl; }
};

class C : public A, public B
{
public:
     void Print() { cout<<"This is class C."<<endl; }
};

int main()
{
     A *pA = new C;
     //C *pC = pA; // Wrong
     C *pC = dynamic_cast<C *>(pA);
     if (pC != NULL)
     {
          pC->Print();
     }
     delete pA;
}

在上面代码中,如果我们直接将pA赋值给pC,这样编译器就会提示错误,而当我们加上了dynamic_cast之后,一切就ok了。那么dynamic_cast在后面干了什么呢?

dynamic_cast主要用于在多态的时候,它允许在运行时刻进行类型转换,从而使程序能够在一个类层次结构中安全地转换类型,把基类指针(引用)转换为派生类指针(引用)。

当类中存在虚函数时,编译器就会在类的成员变量中添加一个指向虚函数表的vptr指针,每一个class所关联的type_info object也经由virtual table被指出来,通常这个type_info object放在表格的第一个slot。

当我们进行dynamic_cast时,编译器会帮我们进行语法检查。如果指针的静态类型和目标类型相同,那么就什么事情都不做;否则,首先对指针进行调整,使得它指向vftable,并将其和调整之后的指针、调整的偏移量、静态类型以及目标类型传递给内部函数。其中最后一个参数指明转换的是指针还是引用。

两者唯一的区别是,如果转换失败,前者返回NULL,后者抛出bad_cast异常。对于在typeid函数的使用中所示例的程序,我使用dynamic_cast进行更改,代码如下:

#include <iostream>
#include <typeinfo>
using namespace std;

class A
{
public:
     virtual void Print() { cout<<"This is class A."<<endl; }
};

class B : public A
{
public:
     void Print() { cout<<"This is class B."<<endl; }
};

class C : public A
{
public:
     void Print() { cout<<"This is class C."<<endl; }
};

void Handle(A *a)
{
     if (dynamic_cast<B*>(a))
     {
          cout<<"I am a B truly."<<endl;
     }
     else if (dynamic_cast<C*>(a))
     {
          cout<<"I am a C truly."<<endl;
     }
     else
     {
          cout<<"I am alone."<<endl;
     }
}

int main()
{
     A *pA = new B();
     Handle(pA);
     delete pA;
     pA = new C();
     Handle(pA);
     return 0;
}

目录
相关文章
|
5月前
|
存储 C语言 C++
c++类型转换与RTTI运行阶段类型识别
我们都知道C++完全兼容C语言,C语言的转换方式很简单,可以在任意类型之间转换,但这也恰恰是缺点,因为极其不安全,可能不经意间将指向const对象的指针转换成非const对象的指针,可能将基类对象指针转成了派生类对象的指针,这种转换很容易出bug,需要严格审查代码才能消除这种隐患,但是C这种转换方式不利于我们审查代码,且程序运行时也可能会出bug。
C++RTTI(运行时类型识别)与异常处理解析
C++RTTI(运行时类型识别)与异常处理解析
83 0
C++实验二:类的继承机制的实现——电梯2.0
实现对第一次实验结果Elevator类的功能扩展。在Elevator类已有功能的基础上派生AdvancedElevator类。AdvancedElevator类可以实现当多人在不同楼层等待乘坐上行或下行的同一部电梯时,能够合理的根据乘坐人的需求对电梯经停的楼层进行排序。
320 0
C++实验二:类的继承机制的实现——电梯2.0
|
编译器 C语言 数据安全/隐私保护
C++ STL开发温习与总结(四): 4.C++面向对象机制的实现
C++ STL开发温习与总结(四): 4.C++面向对象机制的实现
|
人工智能 安全 C++
C++ RTTI中dynamic_cast的用法
我们知道在C++中基类的指针和引用可以指向派生类,因为这是安全的,派生类一定包含了基类 需要的所有的属性和方法,这是向上转换,我们同时还知道虚函数可以随着指针和引用指向的 对象不同而使用不同的方法,这是虚函数的自适应。
1359 0
|
C++ C语言 程序员
C/C++的参数传递机制
原文:C/C++的参数传递机制 近来公司招人较多,由此面试了非常多的C++程序员。面试时,我都会问到参数传递的相关问题,尤其侧重指针。因为指针毕竟是C/C++最重要的一个优势(在某种情况下也可以说是劣势)。
774 0
|
C++
引用内部函数绑定机制,R转义字符,C++引用,别名,模板元,宏,断言,C++多线程,C++智能指针
 1、引用内部函数绑定机制 #include&lt;iostream&gt; #include&lt;functional&gt;   usingnamespacestd; usingnamespacestd::placeholders;   //仿函数,创建一个函数指针,引用一个结构体内部或者一个类内部的共有函数 structMy
1399 0
相关产品
云迁移中心
推荐文章
更多