0

给定以下类层次结构:

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 *
4

3 回答 3

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;
}

由于BC使用虚拟继承,它们只包含指向基的指针,而不包含实际对象。可以看到,因为c是 的开头D,所以它们的地址相同。

于 2013-04-13T05:10:02.353 回答
0

你说得对,B 和 C 需要共享同一个 A 对象。这正是这里会发生的事情。您看到的地址实际上是每个类唯一的虚拟表的地址。在虚拟继承的情况下,每个类的虚拟表都会有一个指向虚拟基类的指针,在这种情况下,是对象 A。

所以类 B 和 C 的虚拟表都有一个指针,每个指针都指向对象 A 的相同地址。

于 2013-04-13T04:47:02.043 回答
0

这只是对象在内存中的表示方式。

所以你的对象看起来像这样:

+ 0x0036f828
- D
- int z (C)
- int y (B) 
- int x (A)

C++ 转换只是给出了对象开头的偏移量。
因此,您可以看到偏移量只是A,B,C类的整数大小(因为它们包含这些)和B,C类的虚拟表。
D没有成员,所以它的偏移量是 0。

请注意,C++ 编译器按照您实际编写成员和基类的顺序设置内存布局。

所以如果你改变 D 中基类的顺序,你会得到不同的结果:

class D : public B, public C

现在B将成为 D 之后布局中的第一个类。

于 2013-04-13T04:55:27.483 回答