C++ RTTI及多态的实现机制
RTTI中无论是dynamic_cast还是typeid,都是只对引用和指针有效。
假设实现多态的继承体系:
class Base
{
public:
virtual void Fun() ;
}
class Derived ;
Base* pp = new Derived() ;
Derived* pp2 = dynamic_cast< Derived*>(pp) ;
pp->Fun() ;
delete pp ;
先看看RTTI 如何实现:Derived* pp2 = dynamic_cast< Derived*>(pp) ;
当执行此语句时,首先找到pp指向的内存,取出此内存中头四个字节的内容,它即是vptr的值,然后找到vptr指向的内存,此处存放的是vtbl,而vtbl的第一个元素就是关于pp所指向实体的类型信息(可以通过此信息来解释pp指向的内存的内容)。到此,就可以决定dynamic_cast< Derived*>(pp)是返回NULL,还是转换成功。
在看多态调用实现: pp->Fun() ;
Fun函数指针在vtbl中的位置是在编译期就确定了的,因为Base类的各虚函数声明后,其vbtl的内容和格式就基本确定了。
原理同上差不多。首先找到pp指向的内存,取出此内存中头四个字节的内容,它即是vptr的值,然后找到vptr指向的内存,此处存放的是vtbl,而vtbl的第二个元素即vptr[1]就是所需要调用的函数的函数指针。
注意:
(1) 各个类的vtbl是在编译期就安排好了的,只是直到运行期才知道某个vbtl占用的内存地址。
(2) 各个vtbl中的函数指针指向的函数是在编译期确定的,但是函数指针的值是在运行期确定的,因为只有到运行期,才会知道函数的代码在内存空间中存放的地址。也可以将各个函数当作一个singleton类来理解。
(3) vptr是由编译器插入到class定义中的,一般作为类的第一个成员。
(4) vptr是在构造函数中被初始化的(这条代码也是编译器加上去了),所以vptr的值是在运行期确定的。可以这样理解:vptr = vtbl.createInstance() ;vtbl实际上是一个singleton类。即一个类对应一个vtbl,改类的所有对象都使用同一个vtbl。