近日在学校bbs上与人讨论C++的typeid关键字的实现问题,有人提到type_info的地址是存放在虚表的第一个位置上,颇觉得不妥,于是我在vc2003下实验了一番
在vc下,使用typeid的时候,如果typeid施加给的类型是没有vptr的class或者根本不是class
那么汇编是
mov dword ptr [addr],offset A `RTTI Type Descriptor' (42AD40h)
也就是编译器生成一个简单的type_info对象的表,并且在编译期静态决定下标,做一个简单查表操作。
如果typeid的操作对象是具有vptr的class,但是并不是一个引用或者指针的解引用形式,例如
A a;
typeid(a);
那么仍然仅仅会做查表操作
如果typeid的操作对象是具有vptr的class,并且是引用或者指针的解引用形式,例如
A * p = new A;
A & r = * p;
typeid( * p);
typeid(r);
那么就会调用一个叫___RTtypeid的函数,并通过某种方法来获取type_info对象
下面是___RTtypeid的反汇编,这里只列出关键的几条指令
0041213E mov ecx,dword ptr [inptr] ;inptr是对象的地址
00412141 mov edx,dword ptr [ecx]
00412143 mov eax,dword ptr [edx - 4 ]
0041215F mov ecx,dword ptr [eax + 0Ch]
00412162 mov dword ptr [ebp - 48h],ecx
0041216C mov eax,dword ptr [ebp - 48h]
基本上等价于C语言的
int a1 = ( int )p; // p是对象的地址
int a2 = * ( int * )a1 - 4 ;
int a3 = * ( int * )a2 + 12 ;
int a4 = * ( int * )a3;
那么从这段代码可以看出vc下type_info对象的存放位置[如下图]
也就虚表下标为-1的位置上存放了一个指向一个未知的表的指针(暂且将此表命名为runtime_info_table)
runtime_info_table的第4格上存放了type_info对象的地址
至于runtime_info_table里前3格上存放的是什么, 还需要再研究研究
一般来说它们全是0, 但是对于多重虚继承的类, 第二格上会是4, 可能和指针的偏移量有关.