浅谈 c++ 类,继承类,有虚函数的类,虚拟继承的类的内存布局,使用vs2010打印布局结果。

使用sizeof函数求类大小这个问题在很多面试,笔试题中很容易考,而涉及到类的时候,又不得不说类的继承,虚继承,虚函数,所以涉及到了类的内存布局,其中关于虚拟继承(virtual public)这个话题比较难以理解,而且不同的编译器环境可能实现的类的内存布局不同,所以本文仅在ms vs2010编译环境下调试,如果你在像cfree这样的编译器中调试结果会不同当涉及到虚拟继承的时候。好了,我们一起来学习吧:

首先关于空类的大小:

class A
{

 };
sizeof(A)==1;

为什么不是0?因为类的实例化,即使是空类,编译器也会默认给类添加一个字节,使其有独一无二的地址。

关于 static 成员,构造,析构,一般成员函数,使用sizeof求类大小的时候,这些是不考虑在内的,因为static成员是所有类对象所共享的,它不是某一个对象的成员,而是具有类的属性,一般成员函数包括构造和析构函数:所以类对象调用时都是同一个函数地址,所以不计算在内。

关于虚函数:

class A{
 int a;
 virtual ~A();
};
sizeof(A)==8;

虚函数大家都知道,一般类实例的最靠前的地址是vfptr(虚函数表指针),这个指针指向这个类的虚函数表(函数指针数组),这个表里放着类的所有虚函数。关于虚函数更多的机理大家可以谷歌之。

所以这里多了一个vfptr指针,共8字节。

关于内存补齐:

class A{
char a;
virtual void f();
};
sizeof(A)==8;

a本来是1个字节,但是这里会有一个<alignment member>(size =3)补齐了3个字节,所以一共8个字节。

下面关于虚拟继承:

class A
{
   public:
   virtual void aa(){};
   char a;
 };
class B:virtual public A
{  
   public:
   virtual void bb(){};
   char b;
};
class C:virtual public A
{
   public:
   virtual void cc(){};
   char c;
};
 class D:public B,public C
{
   public:
   virtual void dd(){};
   char d;
};
sizeof(A)==8;
sizeof(B)==20;
sizeof(C)==20;
sizeof(D)==36;

有点难以理解吧,没关系,我利用vs2010的命令行操作: cl /d1 reportSingleClassLayout类名 程序名.cpp

打印了4个类的内存布局,我们一起来看看:

浅谈 c++ 类,继承类,有虚函数的类,虚拟继承的类的内存布局,使用vs2010打印布局结果。_第1张图片









我们可以看出A的大小为8字节:

vfptr---->(virtual function table pointer)虚函数表指针,4个字节。

a----->成员变量4字节,其中补齐了3个字节。

最后部分为A的虚函数表,我们可以看出A只有一个虚函数:A::aa。

下面看B的内存布局:

浅谈 c++ 类,继承类,有虚函数的类,虚拟继承的类的内存布局,使用vs2010打印布局结果。_第2张图片


B的大小为20:我们需要注意的是B是从A虚拟继承而来的,所以:

vfptr------>自己新增的虚函数表指针4字节;vbprt-------->虚基类表指针,指向B的虚基类4字节;成员变量b,4字节(补齐3个字节);

包含虚基类A的:vfptr------>A的虚函数表指针;成员a(同样补齐3个字节)。

C的内存布局和B一样,下面看D的内存布局:

浅谈 c++ 类,继承类,有虚函数的类,虚拟继承的类的内存布局,使用vs2010打印布局结果。_第3张图片

因为D是从B和C共有继承而来,不是虚继承,所以D没有新增自己的虚函数表指针和虚基类表指针,仅仅包括:

基类B的vfptr,vbptr,b,然后基类C的vfptr,vbptr,c,成员变量d,基类A的vfptr,成员变量a;总共36字节。

我们再看下面一种情况:

class A
{
   public:
   virtual void aa(){};
   char a;
 };
class B:virtual public A
{  
   public:
   virtual void bb(){};
   char b;
};
class C:virtual public A
{
   public:
   virtual void cc(){};
   char c;
};
 class D:virtual public B,virtual public C
{
   public:
   virtual void dd(){};
   char d;
};
sizeof(A)==8;
sizeof(B)==20;
sizeof(C)==20;
sizeof(D)==44;

与上面情况唯一不同的是D,因为它同时从B和C虚继承得到,下面我们看D的内存布局:

浅谈 c++ 类,继承类,有虚函数的类,虚拟继承的类的内存布局,使用vs2010打印布局结果。_第4张图片

这已经很一目了然了吧:由于是虚拟继承,所以在最开始多了自己的一个vfptr和vbptr,其他的没有改变。

下面我们来看最后一种情况:

class A
{
   public:
   virtual void aa(){};
   char a;
 };
class B: {  
   public:
   virtual void bb(){};
   char b;
};
class C:virtual public A, virtual public B
{
   public:
   virtual void cc(){};
   char c;
};

我相信你已经能得出三个类的大小了吧,没错:sizeof(A)==8,sizeof(B)=8,sizeof(C)==28;

还需要我解释吗?看内存布局图吧:

浅谈 c++ 类,继承类,有虚函数的类,虚拟继承的类的内存布局,使用vs2010打印布局结果。_第5张图片


没错,这已经一目了然了。

下面总结下:如果类中有虚函数,则会多一个一个虚函数表指针vfptr,如果是虚继承,则子类会新增一个自己的虚函数表指针,然后还会有一个

指向虚基类表的指针vbptr。这就是c++中类继承中的内存布局情况,如有错误,恳请指正。



你可能感兴趣的:(类,继承,布局,Visual,Studio,2010)