C ++中多继承,虚拟基类和对象大小的问题 [英] Question on multiple inheritance, virtual base classes, and object size in C++

查看:157
本文介绍了C ++中多继承,虚拟基类和对象大小的问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码打印20,即sizeof(z)为20。

  #include< iostream.h> 
class Base
{
public:
int a;
};

class X:public public Base
{
public:
int x;
};

class Y:virtual public Base
{
public:
int y;
};

class Z:public X,public Y
{
};

int main()
{
Z z;
cout<< sizeof(z)<< end1;
}

然而,如果我不在这里使用虚拟基类,代码:
sizeof(z)为16。

  #include< iostream.h> 
class Base
{
public:
int a;
};

class X:public Base
{
public:
int x;
};

class Y:public Base
{
public:
int y;
};

class Z:public X,public Y
{
};

int main()
{
Z z;
cout<< sizeof(z)<< end1;
}

为什么在第一种情况下sizeof
不应该是12,因为Base将包含
只在Z中一次?

解决方案

让我们看看这两种情况的类布局。



没有virtual,你有两个基类(X和Y每个类都集成了一个Base基类,它也有一个整数。这是4个整数,每个32位,共计16个字节。

 偏移大小类型范围名称
0 4 int Base a
4 4 int X x
8 4 int基本a
12 4 int Y y
16个大小(Z成员会在结尾)

(编辑:我在DJGPP中编写了一个程序来获取布局并调整表格以解决它。 / p>

现在让我们谈谈虚拟基类:它们使用指向共享实例的指针替换类的实际实例。你的Z类只有一个Base类,并且X和Y的实例都指向它。因此,你在X,Y和Z中有整数,但你只有一个Z.这意味着你有三个整数,或12字节。但是X和Y也有一个指向共享Z的指针(否则他们不知道在哪里找到它)。在32位机器上,两个指针将添加另外8个字节。这总计20你看到。内存布局可能看起来像这样(我没有验证它... ARM有一个例子,其中排序是X,Y,Z,然后Base):

 偏移大小类型范围名称值(排序)
0 4基本偏移X? 16(或ptr到vtable)
4 4 int X x
8 4基本偏移Y? 16(或ptr到vtable)
12 4 int Y y
16 4 int基本a
20个大小(Z成员会在基本之前)

因此,内存差异是两种情况的组合:一个整数和两个更多的指针。与另一个答案相反,我不相信vtables支付任何(编辑)直接(/编辑)卷,因为没有虚拟函数。



编辑:ppinsider已经提供了关于gcc情况的更多信息,其中他演示gcc通过使用否则为空的vtable(即,没有虚拟函数)来实现指向虚拟基类的指针。这样,如果有虚函数,它不需要在类实例中的额外的指针,需要更多的内存。我怀疑底层是一个额外的间接获得基类。



我们可能希望所有的编译器这样做,但也许不是。 ARM 第225页讨论了虚拟基本类而不提到vtables。特别地址具有虚拟函数的虚拟基类,并且具有指示存储器布局的图,其中存在来自与指向vtable的指针分离的X和Y部分的指针。我建议任何人不要认为指向Base的指针将以表的形式实现。


The following code prints 20, i.e. sizeof(z) is 20.

#include <iostream.h>
class Base
{
      public:
            int a;
};

class X:virtual public Base
{
      public:
            int x;
};

class Y:virtual public Base
{
      public:
            int y;
};

class Z:public X,public Y
{
};

int main()
{
Z z;
cout << sizeof(z) <<endl;
}

Whereas if I don't use virtual base classes here, i.e. for the following code : sizeof(z) is 16.

#include <iostream.h>
class Base
{
      public:
            int a;
};

class X:public Base
{
      public:
            int x;
};

class Y:public Base
{
      public:
            int y;
};

class Z:public X,public Y
{
};

int main()
{
Z z;
cout << sizeof(z) <<endl;
}

Why is sizeof(z) more(20) in the first case? Shouldn't it be 12, since Base will be included only once in Z?

解决方案

Let's look at the class layout of the two cases.

Without the virtual, you have two base classes ("X" and "Y") with an integer each, and each of those classes have integrated into them a "Base" base class which also has an integer. That is 4 integers, 32-bits each, totalling your 16 bytes.

Offset  Size  Type  Scope  Name
     0     4   int   Base     a
     4     4   int      X     x
     8     4   int   Base     a
    12     4   int      Y     y
    16 size (Z members would come at the end)

(Edit: I've written a program in DJGPP to get the layout and tweaked the table to account for it.)

Now let's talk about virtual base classes: they replace the actual instance of the class with a pointer to a shared instance. Your "Z" class has only one "Base" class, and both instances of "X" and "Y" point to it. Therefore, you have integers in X, Y, and Z, but you only have the one Z. That means you have three integers, or 12 bytes. But X and Y also have a pointer to the shared Z (otherwise they wouldn't know where to find it). On a 32-bit machine two pointers will add an additional 8 bytes. This totals the 20 that you see. The memory layout might look something like this (I haven't verified it... the ARM has an example where the ordering is X, Y, Z, then Base):

Offset  Size        Type  Scope  Name  Value (sort of)
     0     4 Base offset      X     ?  16 (or ptr to vtable)
     4     4         int      X     x
     8     4 Base offset      Y     ?  16 (or ptr to vtable)
    12     4         int      Y     y
    16     4         int   Base     a
    20 size (Z members would come before the Base)

So the memory difference is a combination of two things: one less integer and two more pointers. Contrary to another answer, I don't believe vtables pay any (edit) direct (/edit) roll in this, since there are no virtual functions.

Edit: ppinsider has provided more information on the gcc case, in which he demonstrates that gcc implements the pointer to the virtual base class by making use of an otherwise empty vtable (i.e., no virtual functions). That way, if there were virtual functions, it wouldn't require an additional pointer in the class instance, requiring more memory. I suspect the downside is an additional indirection to get to the base class.

We might expect all compilers to do this, but perhaps not. The ARM page 225 discusses virtual base classes without mentioning vtables. Page 235 specifically addresses "virtual base classes with virtual functions" and has a diagram indicating a memory layout where there are pointers from the X and Y parts that are separate from the pointers to the vtable. I would advise anyone not to take for granted that the pointer to Base will be implemented in terms of a table.

这篇关于C ++中多继承,虚拟基类和对象大小的问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆