C++ 类 内存分布 虚函数 单继承 多继承
重点内容
一、首先看看没有继承情况下类的内存分布:
1.1 定义一个简单的类,没有虚函数。
代码如下:
#include<iostream>
using namespace std;
class Base
{
public:
int Base_1;
int Base_2;
public:
void func1();
};
int main(){
Base obj_base;
return 0;
}
编译,得到的内存分布图如下所示:
从这里可以看到普通类的内存排布方式,成员变量依据声明的顺序进行排列,类内偏移从0开始,普通成员函数不占内存空间。这里没有考虑char等成员变量的字节对齐方式等问题。
1.2 定义一个简单的类,包含虚函数。
#include<iostream>
using namespace std;
class Base
{
public:
int Base_1;
int Base_2;
public:
void func1();
virtual void func2(){};
virtual void func3(){};
};
int main(){
Base obj_base;
return 0;
}
编译,得到的内存分布图如下所示:
从图中可以看出,这个内存结构图分成了两个部分,上面是类的内存分布,下面是虚函数表。这里的编译环境VS2013。从结果可知,编译器是将虚表指针{vfptr}放在类内存的开始处,从图可知其偏移量为0,接着放置类其他成员变量,从图可知,Base_1和Base_2的偏移量分别为4,8。下面部分是类的虚函数表。在&Base_meta后面的0表示,这张虚函数表对应的虚指针{vfptr}在类内存中的分布。接着下面列出类的虚函数,虚函数左边表示虚函数的号。
1.3 单一非虚继承。
#include<iostream>
using namespace std;
class Base
{
public:
int Base_1;
int Base_2;
public:
void func1();
virtual void func2(){};
virtual void func3(){};
};
class D :public Base{
public:
int d_1;
public:
virtual void func2(){};
};
int main()
{
return 0;
}
运行结果:
分析:
派生类继承了基类的全部,包括虚表指针{vfptr}和其他变量。同时将继承父类的东西放在类内存的开始处,这样,{vfptr}的地址偏移为0,同时派生类也维护一个自己的虚函数表。总结:在非虚继承下:
(1) 派生类会继承基类的全部,包括虚基指针。
(2) 派生类和基类会各自维护一个虚函数表,他们不相同,不是同一张表。从以上结果体现为,基类虚表为Base::$vftable,派生类的虚表为D::vftable。但是他们虚表对应的虚指针是一样的。
1.4 单一虚继承。
代码:
#include<iostream>
using namespace std;
class Base
{
public:
int Base_1;
int Base_2;
public:
void func1();
virtual void func2(){};
virtual void func3(){};
};
class D :virtual public Base{
public:
int d_1;
public:
virtual void func2(){};
};
int main()
{
return 0;
}
运行结果:
派生类继承了基类的全部,包括虚表指针{vfptr}和其他变量。但是不是将继承父类的东西放在类内存的开始处。在类内存中开始出放置的是自己的虚指针{vbptr},接着自己的成员变量,最后再放置从基类派生下来的数据。此时,派生类有了2个虚指针,一个是自己的虚指针,另一个是从基类派生下来的虚基指针,对应就有2张虚表。各个虚表下面的数字就表示其在类内存中的偏移量。总结:在虚继承下:
(1) 派生类会继承基类的全部,包括虚基指针。但是不是将其放在类内存的地址偏移0处。派生类会新生成虚指针,放在类内存地址偏移量为0处。
(2) 派生类有2个虚指针,对应有两张虚函数表。