目录
0.先说结论
- 在派生类对象的内存中,虚表指针放在最前面,和对象的地址相同。然后是成员变量,基类的成员变量在派生类的成员变量前面,基类和派生类的成员变量分别按类中的声明顺序排列。
- 对于多继承的情况,假如派生类有n个直接基类,那么派生类对象中就有n个虚表指针。派生类对象的内存可以划分为n+1块,首先存放第1个基类的虚表指针和成员变量,然后存放第2个基类的虚表指针和成员变量,以此类推。派生类自己的成员变量放在最后1块。
- 虚表中虚函数的顺序是按声明顺序排列的。基类虚函数的声明先于派生类。
- 派生类的虚函数和第一个直接基类共用一张虚表。并且在这张虚表中,基类的虚函数在前,派生类的虚函数在后。
- 如果派生类覆盖了基类的一个虚函数,那么虚表中本来存放这个基类虚函数地址的位置改为存放派生类版本的虚函数地址。
1.单继承的内存分布
假设继承体系如下:
Derive对象的内存分布如下:
代码验证:
class Base{
public:
virtual f(){cout<<"Base::f"<<endl;} // 被覆盖
virtual h(){cout<<"Base::h"<<endl;} // 不被覆盖
int a;
};
class Derive: public Base{
public:
virtual f(){cout<<"Derive::f"<<endl;}
virtual g(){cout<<"Derive::g"<<endl;}
int b;
};
int main(){
typedef void(*Fun)(void);
Derive d;
int** pd = (int**)&d; // 对象地址,也是虚表指针的地址
cout<<"对象大小:"<<sizeof(Derive)<<endl;
cout<<"对象地址:"<<&d<<endl<<endl;
cout<<"虚表指针地址:"<<pd<<" "<<&pd[0]<<endl;
cout<<"虚表地址:"<<*pd<<endl;
for(int i=0;i<3;i++){ // 第1个虚表
cout<<"函数指针地址:"<<&pd[0][i]<<" "
<<"函数地址:&