上次研究类型转换时的这篇文档最后还讲到了一个运算符 ——typeid,使用这个运算符,会返回一个 type_info 对象的引用,这个对象的结构大致如下:
这个对象包含了一个对象的类型信息,也就是 c++ 中的 RTTI(Runtime Type Identification,即运行时类型识别)。
有趣的是,当 typeid 作用于指针时,会返回指针自身的类型,但当作用于派生类(有虚函数)的对象或引用时,则会返回这个对象实际的类型。
下面是测试代码:
现在有基类 SceneEntry 和派生类 SceneUser
以上代码可以得到以下输出:
可以看到对于指针类型,他返回的就是指针声明时的类型,上面的 P 就表示指针,而引用和类返回的则是他们实际对应的派生类。不过这种获取实际派生类时依赖的是虚函数表,因此没有虚函数的时候也就无从知晓了。
当去掉虚函数以后,输出就变为了:
最终输出的都是基类。
通过了解 type_info,也就基本知道 dynamic_cast 的原理了,其实它就是利用 RTTI 去判断一个指针所指的类实际是什么的,不过这也依赖于虚函数表,所以当你 dynamic_cast 一个没有虚函数的类时就会报错(static_cast 不会受影响):
所以最终,要研究 typeid 和 dynamic_cast 这两个运算符的原理,本质其实是研究 RTTI 的原理,这次主要通过这篇博文来研究,还是增添了不少对 c++ 内存布局的理解的。
我们可以通过以下代码来进行试验:
首先定义两个有继承关系的类,并且定义一个虚函数,然后通过以下代码来验证一个对象的布局:
运行得到以下输出:
由此可以得到 c++ 中的一个包含虚函数的 XX 对象的大致布局:

一个对象的内存第一段会保存一个类的虚函数表的指针,指针指向了虚函数表,即一个函数指针数组,而在这个虚函数表指针指向的地址前一个位置保存了一个 type_info 的指针。而虚函数表指针后面跟着的分别是父类的成员变量和自身的成员变量。
但当我们把虚函数去掉以后,再运行代码就会报错:

因此这种没有多态性质的类内存结构是不同的。这种类的 RTTI 信息在编译后便可以确定,所以猜想可能编译时就带入地址了(不过没查到相关资料,待考证)。因此不需要动态的通过类的虚函数表中的 type_info 去确定。
通过 RTTI 可以让 c++ 在运行时去确定一个对象的实际类型,从而我们可以利用 dynamic_cast 去正确地转换。看到大佬代码用了这么多指针的转换,越来越觉得它是个很自由的语言,它应该是高级语言中最接近底层,最容易操作内存的语言了,不过这也对使用者有较高的要求,学习之路漫漫呀~
版权声明:
本文来源网络,所有图片文章版权属于原作者,如有侵权,联系删除。
本文网址:https://www.mushiming.com/mjsbk/8328.html