4

我知道标准没有指定如何打包数据。我只是想了解类的内存布局(尤其是如何dynamic_cast<void*>保证返回指向最派生类开头的指针)。我想不出关于以下代码输出的任何解释:

struct A{ int a;};
struct B{ int b;};
struct C: public A, public B { int c;};
struct D:public C {int d;};


int main(){
  D* ob=new D;
  A* a = ob;
  B* b = ob;
  C* c = ob;
}

打印指针的值表明 , ,a始终具有相同的值,只是添加了 4 个字节作为偏移量。是偶然的吗?或者背后有什么逻辑?cdb

编辑: 从概念上讲,布局应该像图像,但不知何故,点 A、C 和 D 合并为一个。在此处输入图像描述

4

3 回答 3

8

首先,你的struct A

| int a |

并且B

| int b |

struct C继承struct Aand struct B,并且它也有一个成员,int c。所以它可以有这样的布局:

            struct B
struct A     /
   \        /
| int a | int b | int c |

struct D,它继承struct C,是

            struct B
struct A     /
   \        /
| int a | int b | int c | int d |
\-----------------------/
         struct C

现在想想D* ob = new D;。它会是这样的:

| int a | int b | int c | int d |
^
\
 ob

想想A* a = ob-struct A在偏移量 0 上struct D,所以它是

| int a | int b | int c | int d |
^
\
 a

它等于struct c

但是,当涉及到 时struct B,它位于偏移量 4(如果sizeof(int) == 4)上,所以它是 -

| int a | int b | int c | int d |
        ^
        /
       b

请注意,布局未在标准中定义,并且每个实现都可能不同。我向您展示了一种可能的布局。

有关高级信息,我建议您阅读C++ Multiple Inheritance Memory Layout with "Empty classes"

于 2014-08-05T11:43:36.507 回答
0

你的推理是正确的。但是,标准中没有定义布局,因此您不能依赖它。话虽如此,大多数编译器都会选择您在图中描绘的布局。

于 2014-08-05T11:43:20.927 回答
0

打印指针的值表明,a,c,d 始终具有相同的值,只是 b 增加了 4 个字节作为偏移量。

在 D 对象中,C 子对象首先出现,因此它与完整的 D 对象具有相同的地址也就不足为奇了(您希望在 C 对象之前出现什么?为什么在D的开始?)

在 C 对象中,A 子对象首先出现,因此它与 C 对象具有相同的地址也就不足为奇了,因此如果 C 是 D 的子对象,它也具有相同的地址作为完整的 D 对象。

是偶然的吗?或者背后有什么逻辑?

这不是偶然的。它由编译器的 ABI 定义。许多编译器遵循Itanium C++ ABI,它记录了类的布局方式。

于 2014-08-05T13:20:05.720 回答