2025年2月24日 星期一 甲辰(龙)年 腊月廿四 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > VC/VC++

C++ RTTI机制(C++运行时类型识别)

时间:01-05来源:作者:点击数:985

一般情况下,在编译期间就能确定一个表达式的类型,但是当存在多态时,有些表达式的类型在编译期间就无法确定了,必须等到程序运行后根据实际的环境来确定。下面的例子演示了这种情况:

  • #include <iostream>
  • using namespace std;
  • //基类
  • class Base{
  • public:
  • virtual void func();
  • protected:
  • int m_a;
  • int m_b;
  • };
  • void Base::func(){ cout<<"Base"<<endl; }
  • //派生类
  • class Derived: public Base{
  • public:
  • void func();
  • private:
  • int m_c;
  • };
  • void Derived::func(){ cout<<"Derived"<<endl; }
  • int main(){
  • Base *p;
  • int n;
  • cin>>n;
  • if(n <= 100){
  • p = new Base();
  • }else{
  • p = new Derived();
  • }
  • cout<<typeid(*p).name()<<endl;
  • return 0;
  • }

输入 45,运行结果为:

45↙
class Base

输入 130,运行结果为:

130↙
class Derived

基类 Base 包含了一个虚函数,派生类 Derived 又定义了一个原型相同的函数遮蔽了它,这就构成了多态。p 是基类的指针,可以指向基类对象,也可以指向派生类对象;*p表示 p 指向的对象。

从代码中可以看出,用户输入的数字不同,*p表示的对象就不同,typeid 获取到的类型也就不同,编译器在编译期间无法预估用户的输入,所以无法确定*p的类型,必须等到程序真的运行了、用户输入完毕了才能确定*p的类型。

根据前面讲过的知识,C++ 的对象内存模型主要包含了以下几个方面的内容:

  • 如果没有虚函数也没有虚继承,那么对象内存模型中只有成员变量。
  • 如果类包含了虚函数,那么会额外添加一个虚函数表,并在对象内存中插入一个指针,指向这个虚函数表。
  • 如果类包含了虚继承,那么会额外添加一个虚基类表,并在对象内存中插入一个指针,指向这个虚基类表。

现在我们要补充的一点是,如果类包含了虚函数,那么该类的对象内存中还会额外增加类型信息,也即 type_info 对象。以上面的代码为例,Base 和 Derived 的对象内存模型如下图所示:

编译器会在虚函数表 vftable 的开头插入一个指针,指向当前类对应的 type_info 对象。当程序在运行阶段获取类型信息时,可以通过对象指针 p 找到虚函数表指针 vfptr,再通过 vfptr 找到 type_info 对象的指针,进而取得类型信息。下面的代码演示了这种转换过程:

**(p->vfptr - 1)

程序运行后,不管 p 指向 Base 类对象还是指向 Derived 类对象,只要执行这条语句就可以取得 type_info 对象。

编译器在编译阶段无法确定 p 指向哪个对象,也就无法获取*p的类型信息,但是编译器可以在编译阶段做好各种准备,这样程序在运行后可以借助这些准备好的数据来获取类型信息。这些准备包括:

  • 创建 type_info 对象,并在 vftable 的开头插入一个指针,指向 type_info 对象。
  • 将获取类型信息的操作转换成类似**(p->vfptr - 1)这样的语句。

这样做虽然会占用更多的内存,效率也降低了,但这是没办法的事情,编译器实在是无能为力了。

这种在程序运行后确定对象的类型信息的机制称为运行时类型识别(Run-Time Type Identification,RTTI)。在 C++ 中,只有类中包含了虚函数时才会启用 RTTI 机制,其他所有情况都可以在编译阶段确定类型信息。

下面是 RTTI  机制的一个具体应用,可以让代码根据不同的类型进行不同的操作:

  • #include <iostream>
  • using namespace std;
  • //基类
  • class People{
  • public:
  • virtual void func(){ }
  • };
  • //派生类
  • class Student: public People{ };
  • int main(){
  • People *p;
  • int n;
  • cin>>n;
  • if(n <= 100){
  • p = new People();
  • }else{
  • p = new Student();
  • }
  • //根据不同的类型进行不同的操作
  • if(typeid(*p) == typeid(People)){
  • cout<<"I am human."<<endl;
  • }else{
  • cout<<"I am a student."<<endl;
  • }
  • return 0;
  • }

可能的运行结果:

83↙
I am human.

多态(Polymorphism)是面向对象编程的一个重要特征,它极大地增加了程序的灵活性,C++、C#、Java 等“正统的”面向对象编程语言都支持多态。但是支持多态的代价也是很大的,有些信息在编译阶段无法确定下来,必须提前做好充足的准备,让程序运行后再执行一段代码获取,这会消耗更多的内存和 CPU 资源。

方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门