了解虚拟派生类的大小

the*_*z03 2 c++ multiple-inheritance virtual-inheritance memory-layout

#include <iostream>
using namespace std;
class A {
  int a;
};
class B1 : virtual public A {
  int b1;
};
class B2 : virtual public A {
  int b2;
};
class C : public B1, public B2 {
  int c;
};

int main() {
  A obj1; B1 obj2; B2 obj3; C obj4;
  cout << sizeof(obj1) << endl;
  cout << sizeof(obj2) << endl;
  cout << sizeof(obj3) << endl;
  cout << sizeof(obj4) << endl;
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出:

4
16
16
40
Run Code Online (Sandbox Code Playgroud)

在上面的 c++ 程序中,A 的大小是 4,因为它只有一个 int,B1 的大小是 16,因为 (int+ int + 虚拟指针) 与 B2 相同

但是为什么C的大小是40???

ale*_*ame 5

结果可能因您使用的编译器和系统架构而异。例如,msvc 19.28 x64给出这个不言自明的结果(使用选项/d1reportAllClassLayout得到它):

class C size(44):
    +---
 0  | +--- (base class B1)
 0  | | {vbptr}
 8  | | b1
    | | <alignment member> (size=4)
    | | <alignment member> (size=4)
    | +---
16  | +--- (base class B2)
16  | | {vbptr}
24  | | b2
    | | <alignment member> (size=4)
    | | <alignment member> (size=4)
    | +---
32  | c
    | <alignment member> (size=4)
    +---
    +--- (virtual base A)
40  | a
    +---
Run Code Online (Sandbox Code Playgroud)

msvc 19.28 x86结果将是:

class C size(24):
    +---
 0  | +--- (base class B1)
 0  | | {vbptr}
 4  | | b1
    | +---
 8  | +--- (base class B2)
 8  | | {vbptr}
12  | | b2
    | +---
16  | c
    +---
    +--- (virtual base A)
20  | a
    +---
Run Code Online (Sandbox Code Playgroud)

更新

需要注意的是,上面的类布局清楚地展示了虚继承的特点,即基类实例(A)的一个副本被孙子派生类(C)继承。如果A::a数据成员是公共的,我们可以在类的成员函数中使用以下语句C

void C::foo() {
    B2::a = 1;
    B1::a = 2;
    std::cout << B2::a << " " << B1::a << " " << A::a; // Output: 2 2 2
}
Run Code Online (Sandbox Code Playgroud)

但是如果你不使用虚拟继承(而只是public),那么类的C布局将是这样的:

class C size(20):
    +---
 0  | +--- (base class B1)
 0  | | +--- (base class A)
 0  | | | a
    | | +---
 4  | | b1
    | +---
 8  | +--- (base class B2)
 8  | | +--- (base class A)
 8  | | | a
    | | +---
12  | | b2
    | +---
16  | c
    +---
Run Code Online (Sandbox Code Playgroud)

我们会有两个基类成员变量的副本 A

void C::foo() {
    B2::a = 1;
    B1::a = 2;
    std::cout << B2::a << " " << B1::a; // Output: 1 2. `A::a` ambiguity error
}
Run Code Online (Sandbox Code Playgroud)