先引用兩句名言作為開場白:
"C++老手分兩類:一種人把語言用得爛熟,OO觀念也有;另一種人不但如此,還對於台面下的機制,如編譯器合成的default constructor、object的內存布局等有莫大的興趣。"
"了解C++對象模型,絕對有助於你在語言本身以及面向對象觀念兩方面的層次提升。"
首先以Point類為例
再看下對象模型
這個模型好像跟我們以往的認知有些不一致呀,vtable首四個字節怎麼是type_info的指針?type_info是RTTI的內容,是編譯器用來進行類型識別的,它到底存不存在呢?
用代碼測試一下
[cpp]
// 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;
}
// 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會報錯,可能對象模型不一致)
[cpp]
#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;
}
#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;
}