我知道标准没有指定如何打包数据。我只是试图了解类的内存布局(尤其是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
,c
,d
始终具有相同的值,仅添加b
4个字节作为偏移量。这是偶然的吗?或者背后有一个逻辑?
修改 从概念上讲,布局应该像图像一样,但不知何故,点A,C和D合并为一个。
答案 0 :(得分:6)
首先,您的struct A
是
| int a |
和B
是
| int b |
struct C
继承struct A
和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
位于struct D
的偏移0处,所以它是
| 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"。
答案 1 :(得分:0)
你的推理是正确的。但是,标准中没有定义布局,因此您不能依赖它。话虽这么说,大多数编译器会选择你在图中描绘的布局。
答案 2 :(得分:0)
打印指针的值表明,a,c,d始终具有相同的值,只有b被添加4个字节作为偏移量。
在D对象中,C子对象首先出现,所以它与完整的D对象具有相同的地址并不奇怪(你期望在C对象之前出现什么?为什么还有任何额外的对象? D?开头的字节数?)
在C对象中,A子对象首先出现,因此它与C对象具有相同的地址并不奇怪,因此如果C是D的子对象,它也有与完整D对象相同的地址。
是偶然的吗?或者背后有一个逻辑?
这不是偶然的。它由您的编译器的ABI定义。许多编译器遵循Itanium C++ ABI,它记录了必须如何布置类。