虚拟多重继承和指针

Virtual multiple inheritance and pointers

本文关键字:指针 多重继承 虚拟      更新时间:2023-10-16

给定以下类层次:

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

和下面的main:

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 *

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

所以你的对象是这样的

+ 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之后的第一个类。

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

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

如果用普通结构体写出来,结果可能是这样的:

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的开头,它们将具有相同的地址。