The result may differ depending on the compiler you are using and the system architecture. For example, msvc 19.28 x64 gives this self-explanatory result (use the option /d1reportAllClassLayout to get it):
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
+---
But for msvc 19.28 x86 the result would be:
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
+---
Update
It should be noted that the class layout above clearly demonstrates the feature of virtual inheritance, in which only one copy of a base class instance (A) are inherited by grandchild derived class (C). If the A::a data member were public, we could use the following statements inside member function of class C:
void C::foo() {
B2::a = 1;
B1::a = 2;
std::cout << B2::a << " " << B1::a << " " << A::a; // Output: 2 2 2
}
But if you do not use virtual inheritance (but just public), then the class C layout will be like this:
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
+---
And we would have two copies of member variables of the base class A
void C::foo() {
B2::a = 1;
B1::a = 2;
std::cout << B2::a << " " << B1::a; // Output: 1 2. `A::a` ambiguity error
}