The problem is in structure memory alignment. For more details check this atricle: http://en.wikipedia.org/wiki/Data_structure_alignment
As you can see the memory padding depends on your platform.
Explanation:
Let's try to debug memory used for your class.
As we know variables are not virtuals. That means there is no record of vintrual table to determine which variable to use. So compiler does not need to create virtual functions table unless you have no virtual functions.
On my machine virtual functions table pointer member is called __vfptr. On debug you will not be able to find the member with this name unless you add an virtual function. That means you should remove virtual functions pointer size from you calculations.
Try to run following code and read the comments for details:
struct Empty
{
};
struct VirtualNotEmpty: public virtual Empty
{
char c;
};
struct VirtualNotEmptyEx: public virtual Empty
{
char c1;
char c2;
char c3;
char c4;
};
struct TrueVirtualNotEmpty: public virtual Empty
{
virtual void f() {}
char c;
};
void inspect_vne()
{
VirtualNotEmpty vne;
char* start_of_struct = (char*)(&vne);
char* end_of_struct = (char*)(&vne);
end_of_struct += sizeof(vne);
char* start_of_var = (char*)(&(vne.c));
printf("sizeof(VirtualNotEmpty): %d\n", sizeof(vne)); // 8 bytes
printf("Address of struct %p\n", start_of_struct);
printf("Lenght of struct %d\n", (int)(end_of_struct - start_of_struct)); // same as sizeof
printf("Address of var c %p\n", start_of_var);
printf("Diff from start: %d\n", (int)(start_of_var - start_of_struct)); // 4 bytes: 1 byte for struct and 3 bytes for padding
printf("Diff from end: %d\n", (int)(end_of_struct - start_of_var)); // 4 bytes: 1 byte for struct and 3 bytes for padding
// [st][pd][pd][pd] [ch][pd][pd][pd]
// st -- struct vt -- virtual table, pd -- padding byte ch -- char member variable
}
void inspect_tvne()
{
TrueVirtualNotEmpty vne;
char* start_of_struct = (char*)(&vne);
char* end_of_struct = (char*)(&vne);
end_of_struct += sizeof(vne);
char* start_of_var = (char*)(&(vne.c));
printf("sizeof(TrueVirtualNotEmpty): %d\n", sizeof(vne)); // 12
printf("Address of struct %p\n", start_of_struct);
printf("Lenght of struct %d\n", (int)(end_of_struct - start_of_struct)); // same as sizeof
printf("Address of var c %p\n", start_of_var);
printf("Diff from start: %d\n", (int)(start_of_var - start_of_struct)); // 8 bytes: 1 byte for struct + 3 bytes of struct padding + 4 bytes o vptr
printf("Diff from end: %d\n", (int)(end_of_struct - start_of_var)); // 4 bytes: 1 byte for member variable + 3 bytes for padding
// [st][pd][pd][pd] [vt][vt][vt][vt] [ch][pd][pd][pd]
// st -- struct vt -- virtual table, pd -- padding byte ch -- char member variable
// member variable TrueVirtualNotEmpty::c can't be packed at first byte, because of order of dectaled members __vptr is declared bevore
// TrueVirtualNotEmpty::c
}
int main(int, char**)
{
printf("Size of pointer %d", sizeof(int*)); // 4 on my machine
inspect_vne();
inspect_tvne();
printf("Size of VirtualNotEmptyEx %d\n", sizeof(VirtualNotEmptyEx)); // there is no need to create padding at
// end for c1-c4 chars because there is space to pack them
return 0;
}
As you can see size of struct on my machine should be dividable by 4.