#include <iostream>
using namespace std;
struct A{
virtual void f(){};
int a;
char ch;
};
struct B : public A{
char d;
};
struct C{
double dd;
int a;
char ch;
};
struct D : public C{
char d;
};
int main(){
B b;
D d;
cout<<"b="<<sizeof(b)<<endl;
cout<<"d="<<sizeof(d)<<endl;
}
Why are the results of b and d different? The actual result is below:
b=16
d=24
I think that the offset of each member relative to the address of the structure variable is exactly a multiple of the size of that member's data type, and the final size is a multiple of the largest data type size among the members. In the presence of inheritance, base class members always appear before derived class members. Furthermore, even with byte alignment, derived class members do not occupy the padding bytes reserved for the base class. In other words, once the size of the base class is determined, these bytes are exclusively owned by the base class and cannot be utilized by members of the derived class. So why the sizeof(b) is 16 instead of 24?
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:
Output:
Here we see the issue! The
struct Ahas three fields: a pointer to the class "vtable" (clearly 32 bits, here), anint(also 4 bytes) and achar(that last field is padded to 4 bytes); 3 x 4 bytes = 12 bytes; add 4 bytes for the (padded)charfield of the derived class and you have 16 bytes.However, although
struct Balso has three fields, the size of the first (adouble, 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,dandA,Cpairs, because the pointer is then the same size (8 bytes) as thedouble.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 Ais 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).