What you say about the sizes of structures with inherited base classes may be correct1, although I'm not sure that every assertion you make is true for "non-POD" base classes, like A
(because of the member function). However, what you haven't done (and should) is check the sizes of the two (different) base classes.
I can compile your given code on a platform that reproduces your size values (i.e., MSVC, targeting 32-bit Windows); in that case, I added a couple of lines to show the sizes of the base classes:
// Earlier code as yours ...
int main() {
B b;
D d;
cout << "b=" << sizeof(b) << endl;
cout << "d=" << sizeof(d) << endl;
cout << "A=" << sizeof(A) << endl;
cout << "C=" << sizeof(C) << endl;
return 0;
}
Output:
b=16
d=24
A=12
C=16
Here we see the issue! The struct A
has three fields: a pointer to the class "vtable" (clearly 32 bits, here), an int
(also 4 bytes) and a char
(that last field is padded to 4 bytes); 3 x 4 bytes = 12 bytes; add 4 bytes for the (padded) char
field of the derived class and you have 16 bytes.
However, although struct B
also has three fields, the size of the first (a double
, presumably IEEE-conformant) is 8 bytes, which increases the size of that structure to 16 bytes, and of that derived from it to 24.
When I run the same code built for a 64-bit system, I get the same sizes for the b
, d
and A
, C
pairs, because the pointer is then the same size (8 bytes) as the double
.
EDIT: The above explanation works for the specific compiler/platform I used and may be the explanation for your observation. However, as pointed out in the comments (here and on the question), the GNU g++ compiler – when targeting 64-bit Windows – gives the same sizes for the two base classes (as expected) but still gives diferent sizes for the derived classes. [See it on Compiler Explorer]
The issue here is clearly different and, as mentioned in the comments and the various other Stack Overflow posts linked therein, is likely because g++ has recognized that struct A
is not a "POD-type" and, accordingly, uses the space from its "tail padding" in the derived class.
1 As far as I can tell, the C++ Standard does not prohibit the re-use of tail padding space in derived classes, even for "POD" types. However, compilers may avoid doing so (as g++ does in the linked example) on PODs because it may prevent optimization and/or simplification when using memory-copy operations between base and derived classes (such copy operations should not be used on classes with virtual functions because they would involve overwriting the vtable entries).
Further discussion here: C++ + gcc: tail padding reuse and PODs (and in posts there linked).