先引用两句名言作为开场白:
"C++老手分两类:一种人把语言用得烂熟,OO观念也有;另一种人不但如此,还对于台面下的机制,如编译器合成的default constructor、object的内存布局等有莫大的兴趣。"
"了解C++对象模型,绝对有助于你在语言本身以及面向对象观念两方面的层次提升。"
首先以Point类为例
再看下对象模型
这个模型好像跟我们以往的认知有些不一致呀,vtable首四个字节怎么是type_info的指针?type_info是RTTI的内容,是编译器用来进行类型识别的,它到底存不存在呢?
用代码测试一下
// VirtualTable.cpp : Defines the entry point for the console application. // #include "stdafx.h" #include <iostream> using namespace std; class Base { private: int x; public: virtual void f() { cout<<"Base::f"<<endl; }; virtual void g() { cout<<"Base::g"<<endl; }; virtual void h() { cout<<"Base::h"<<endl; }; Base(){x = 2;}; }; class Derive : public Base{ private: int y; public: virtual void f1() { cout<<"Derive::f1"<<endl; }; virtual void g1() { cout<<"Derive::g1"<<endl; }; virtual void h1() { cout<<"Derive::h1"<<endl; }; Derive(){y = 3;}; }; int main(int argc, char* argv[]) { typedef void(*Fun)(void); Base b; Derive d; Fun pFun = NULL; cout <<"虚函数表入口指针地址"<< (int*)(&b) << endl; cout <<"虚函数表的地址"<< (int*)*(int*)(&b) << endl; cout <<"Base — 第一个数据成员"<< *((int*)(&b)+1) << endl; cout <<"Derive — 第一个数据成员"<< *((int*)(&d)+1) << endl; cout <<"Derive — 第二个数据成员"<< *((int*)(&d)+2) << endl; // Invoke the first virtual function pFun = (Fun)*((int*)*(int*)(&b)); pFun = (Fun)*((int*)*(int*)(&b)+0); // Base::f() pFun(); pFun = (Fun)*((int*)*(int*)(&b)+1); // Base::g() pFun(); pFun = (Fun)*((int*)*(int*)(&b)+2); // Base::h() pFun(); //以上代码说明 虚函数表位于对象内存的最开始的位置 pFun = (Fun)*((int*)*(int*)(&d)+0); // Base::f() pFun(); pFun = (Fun)*((int*)*(int*)(&d)+1); // Base::g() pFun(); pFun = (Fun)*((int*)*(int*)(&d)+2); // Base::h() pFun(); pFun = (Fun)*((int*)*(int*)(&d)+3); // Derive::f1() pFun(); pFun = (Fun)*((int*)*(int*)(&d)+4); // Derive::g1() pFun(); pFun = (Fun)*((int*)*(int*)(&d)+5); // Derive::h1() pFun(); system("pause"); return 0; }
没有看到 type_info的影子,这也正是我们以往心目中Vtable的内存位置
这是神马情况?上文的图在胡扯?
再看下面代码,
测试代码(vc8.0执行正常,vc6会报错,可能对象模型不一致)
#include "iostream" #include "string" using namespace std; class Aclass { public: int a; virtual void setA(int tmp) { a=tmp; cout<<a<<endl; } }; class Bclass:public Aclass { public: virtual void setA(int tmp) { a=tmp+10; cout<<a<<endl; } public: void print() { cout<<a<<endl; } }; class Cclass:public Bclass { }; typedef unsigned long DWORD; struct TypeDescriptor { DWORD ptrToVTable; DWORD spare; char name[8]; }; struct PMD { int mdisp; //member displacement int pdisp; //vbtable displacement int vdisp; //displacement inside vbtable }; struct RTTIBaseClassDescriptor { struct TypeDescriptor* pTypeDescriptor; //type descriptor of the class DWORD numContainedBases; //number of nested classes following in the Base Class Array struct PMD where; //pointer-to-member displacement info DWORD attributes; //flags, usually 0 }; struct RTTIClassHierarchyDescriptor { DWORD signature; //always zero? DWORD attributes; //bit 0 set = multiple inheritance, bit 1 set = virtual inheritance DWORD numBaseClasses; //number of classes in pBaseClassArray struct RTTIBaseClassArray* pBaseClassArray; }; struct RTTICompleteObjectLocator { DWORD signature; //always zero ? DWORD offset; //offset of this vtable in the complete class DWORD cdOffset; //constructor displacement offset struct TypeDescriptor* pTypeDescriptor; //TypeDescriptor of the complete class struct RTTIClassHierarchyDescriptor* pClassDescriptor; //describes inheritance hierarchy }; int main( ) { Aclass* ptra=new Bclass; int ** ptrvf=(int**)(ptra); RTTICompleteObjectLocator str = *((RTTICompleteObjectLocator*)(*((int*)ptrvf[0]-1))); //abstract class name from RTTI string classname(str.pTypeDescriptor->name); classname=classname.substr(4,classname.find("@@")-4); cout<<classname<<endl; system("pause"); return 0; }
感谢 点击打开链接 的文章<<对象与对象的类型信息----获取对象的RTTI信息>>,引用了你的代码。