给定以下类层次结构:
class A {
int x;
public:
A(int X) : x(X) {}
void setX(int x) { this->x = x; }
};
class B : public virtual A {
int y;
public:
B(int X, int Y) : A(X), y(Y) {}
};
class C : public virtual A {
int z;
public:
C(int X, int Z) : A(X), z(Z) {}
};
class D : public C, public B {
public:
D(int x, int y, int z) : A(x), C(x,z), B(x,y) {}
};
以及以下主要内容:
int main (void)
{
D x(2,3,4);
A* temp1 = &x;
B* temp2 = &x;
C* temp3 = &x;
}
似乎temp1,temp2和temp3都指向不同的地址.. B和C不应该共享同一个A对象吗? 毕竟,每个C和B对象都是A,所以指针应首先“看到”A对象..不是吗? 另外,C指针包含X的地址..这是一个D对象。为什么呢?
这是内存映射:
&x 0x0036f828 {...} D *
temp1 0x0036f838 {x=5 } A *
temp2 0x0036f830 {y=3 } B *
temp3 0x0036f828 {z=4 } C *
答案 0 :(得分:0)
你是对的,因为B和C需要共享同一个A对象。而这正是这里会发生的事情。您看到的地址实际上是每个类唯一的虚拟表的地址。在虚拟继承的情况下,每个类的虚拟表将具有指向虚拟基类的指针,在本例中为对象A.
因此,B类和C类的虚拟表都有一个指针,每个指针都指向对象A的相同地址。
答案 1 :(得分:0)
这就是对象represented in memory的方式。
所以你的对象看起来像这样:
+ 0x0036f828
- D
- int z (C)
- int y (B)
- int x (A)
C ++ cast只是给出了对象开头的偏移量
因此,您可以看到偏移量只是类 A,B,C 的整数大小(因为它是它们包含的内容)和类 B,C 。
并且 D 没有成员,因此其偏移量为0。
请注意,C ++编译器按实际编写成员和基类的顺序设置内存布局。
因此,如果你改变D中基类的顺序,那么你会得到不同的结果:
class D : public B, public C
现在 B 将是D。
之后布局中的第一个类答案 2 :(得分:0)
如果使用普通结构写出来,这就是看起来的样子:
struct A {
int x;
};
struct B {
A *ap;
int y;
};
struct C {
A *ap;
int z;
};
struct D {
C c;
B b;
A a;
};
int main (void)
{
D x;
A* temp1 = &x.a;
B* temp2 = &x.b;
C* temp3 = &x.c;
}
由于B
和C
使用虚拟不一致,它们只包含指向基础的指针,而不是实际对象。您可以看到c
位于D
的开头,因此它们将具有相同的地址。