c++對象模型
首先看這樣一個類
class Test{
};

TestSize.png
- 在c++中,對于一個空類,c++不允許什么都不存在,會有一個字節(jié)的占位空間。
如果類中有成員變量
class Test{
char a[2];
};

TestSizeChar.png
- 現(xiàn)在看似很正常
如果類中有函數(shù)
class Test{
public:
void fun()
{
}
};

TestSize.png
如果類中有虛函數(shù)
class Test{
public:
virtual void fun()
{
}
};

TestSizeVirtual.png
- 此時,c++會在類的中產(chǎn)生一個指針,這是一個函數(shù)指針,指向虛函數(shù)--虛表,因為虛函數(shù)要實現(xiàn)一種動態(tài)綁定,而其他函數(shù)不需要,所以,有虛函數(shù)存在的時候,類的對象中會有一個指針來指向所有虛函數(shù)。
如果類中有虛函數(shù)和成員變量
class Test{
public:
char a[2];
virtual void fun(){}
};

TestSizeVirtualChar.png
- 按理說
sizeof(Test)應(yīng)該是2+8=10,但是卻是16,這是因為,此時會發(fā)生內(nèi)存對齊,為了效率,會讓內(nèi)存的大小按照最大的字段的整數(shù)倍進(jìn)行對齊。
驗證虛表
class TestModel{
public:
virtual void test(){
cout<<"this="<<this<<endl;
cout<<"test"<<endl;
my();
}
virtual void test1(){
cout<<"this="<<this<<endl;
cout<<"test1"<<endl;
myTest();
}
TestModel(){
cout<<"構(gòu)造"<<endl;
}
virtual ~TestModel(){
cout<<"析構(gòu)"<<endl;
}
int i;
private:
void myTest(){
cout<<"myTest"<<endl;
}
void my(){
cout<<"my"<<i<<endl;
}
};
對于上面這樣一個類,他的內(nèi)存分布情況如下
+---------+
| other |---> 其他字段
|---------|
| vptr |---> 虛表 ------->
+---------+ +-----------------------------+
0| test(TestModel* this) |
|-----------------------------|
1| test1(TestModel* this) |
|-----------------------------|
2| ~TestModel(TestModel* this)|
+-----------------------------+
測試代碼
#include <iostream>
#include <stdio.h>
using namespace std;
class TestModel{
public:
virtual void test(){
cout<<"this="<<this<<endl;
cout<<"test"<<endl;
my();
}
virtual void test1(){
cout<<"this="<<this<<endl;
cout<<"test1"<<endl;
myTest();
}
TestModel(){
cout<<"構(gòu)造"<<endl;
}
virtual ~TestModel(){
cout<<"析構(gòu)"<<endl;
}
int i;
private:
void myTest(){
cout<<"myTest"<<endl;
}
void my(){
cout<<"my"<<i<<endl;
}
};
typedef void (*Fun)(void* self);
void fun(){
cout<<"fun"<<endl;
}
int main(int argc, char const *argv[])
{
TestModel a;
TestModel b;
a.i=0;
b.i=1;
int64_t* pa = (int64_t*)&a;
int64_t* pb = (int64_t*)&b;
void* p1;
printf("指針大小%d\n",sizeof(void*));
printf("a的地址%p\n",pa);
printf("a的虛表地址%p\n",*pa);
printf("b的地址%p\n",pb);
printf("b的虛表地址%p\n",*pa);
Fun funp;
funp = (Fun)(*((int64_t*)(*(pa))));
funp(pa);
funp = (Fun)(*((int64_t*)(*(pa))+1));
funp(NULL);
funp = (Fun)(*((int64_t*)(*(pa))+2));
funp(pa);
return 0;
}

TestSizeMain.png
+---------+
| vptr |---> 虛表 -------> <---*pa==*pb
|---------| +-----------------------------+
| other | 0| test(TestModel* this) | *((int64_t*)(*(pa)))
+---------+ |-----------------------------|
1| test1(TestModel* this) | *((int64_t*)(*(pa))+1)
|-----------------------------|
2| ~TestModel(TestModel* this)| *((int64_t*)(*(pa))+2)
+-----------------------------+
- 測試代碼中,用一個函數(shù)指針指向虛表的各個函數(shù),就能調(diào)用到對應(yīng)的函數(shù),說明正確的找到了函數(shù)
- 并且通過傳入的函數(shù)的指針值不同,調(diào)用的函數(shù)中打印出來的i不同,可以說明
this指針是在函數(shù)調(diào)用的時候通過參數(shù)傳入的