C++ - sizeof function applied to object class - c++

I received the following question which was related to sizeof() :
class C
{
public:
C();
virtual ~C();
unsigned char _member0 s[4];
static long _member1 d;
}
int main()
{
C vc;
cout << sizeof(vc);
}
Can someone explain how the sizeof() function is evaluated in this case?

The exact answer could vary from compiler to compiler so in strict sense the answer to your question is this is Implementation Defined.
Considering this to be an interview Q(saw your previous Q), You should have pointed out the following points:
A compiler is allowed to add padding bytes to a structure/class,this might add to the size.
A compiler might add vptr to an class instance,this might add to the size.
The class members will occupy memory.
static members do not contribute towards the size of an class object because they do not belong to an instance of class but to the class.

It gives the size of vc. vc is of class C. Each object of class C contains metadata (pointer to vtable), since C contains virtual methods. In addition, C has a data field (the character array).
Hence the size of vc should be the size of a pointer plus four bytes (plus padding, see comment below, thanks).
d is not component of an object of class C, since it is static, hence it does not count.
So we have:
------vc--------- ----vtable for C---- ----statics----
| ptr to vtable | ----------> | pointer to ~C | | C::d |
|---------------| | ... | | ... |
| char [4] | -------------------- ---------------
-----------------

Related

Pointer casting offset of a class with single inheritance

While learning the "Effective C++", I was firstly surprised when I learned the fact that if a class had multiple inheritance, its pointer may take offset when the pointer casting is done. Although it was not easy concept to grasp, but I think I managed to get it.
However, the author claims that this offset might happen even in the pointer casting of singly inherited class. I wonder what would be the such case, and wish to know the rationale behind it.
This can happen when polymorphism is introduced into a class hierarchy by a derived class. Consider the following class:
struct Foo
{
int a;
int b;
};
This class is not polymorphic, and thus the implementation does not need to include a pointer to a virtual dispatch table (a commonly-used method of implementing virtual dispatch). It will be laid out in memory like this:
Foo
+---+
a | |
+---+
b | |
+---+
Now consider a class that inherits from Foo:
struct Bar : Foo
{
virtual ~Bar() = default;
};
This class is polymorphic, and so objects of this class need to include a pointer to a vtable so further derived classes can override Bar's virtual member functions. That means that Bar objects will be laid out in memory like this:
Bar
+---------+
vtable pointer | |
+---------+
Foo subobject | +---+ |
| a | | |
| +---+ |
| b | | |
| +---+ |
+---------+
Since the object's Foo subobject is not at the beginning of the object, any Foo* initialized from a pointer to a Bar object will need to be adjusted by the size of a pointer so that it actually points at the Bar object's Foo subobject.
Live Demo
class B {
int a = 0;
};
class D : public B {
virtual ~D() = default;
};
D has a virtual member. B does not. A common implementation of dynamic dispatch in C++ involves keeping a hidden pointer to a table of function addresses at the begining of the object.
This means the first byte of the B sub-object won't be at the start of the complete D object. A pointer cast would need to adjust the address by the vptr size.

object size of class which is inherited from virtual base classes? [duplicate]

I have some questions about the object size with virtual.
1) virtual function
class A {
public:
int a;
virtual void v();
}
The size of class A is 8bytes....one integer(4 bytes) plus one virtual pointer(4 bytes)
It's clear!
class B: public A{
public:
int b;
virtual void w();
}
What's the size of class B? I tested using sizeof B, it prints
12
Does it mean that only one vptr is there even both of class B and class A have virtual function? Why there is only one vptr?
class A {
public:
int a;
virtual void v();
};
class B {
public:
int b;
virtual void w();
};
class C : public A, public B {
public:
int c;
virtual void x();
};
The sizeof C is 20........
It seems that in this case, two vptrs are in the layout.....How does this happen? I think the two vptrs one is for class A and another is for class B....so there is no vptr for the virtual function of class C?
My question is, what's the rule about the number of vptrs in inheritance?
2) virtual inheritance
class A {
public:
int a;
virtual void v();
};
class B: virtual public A{ //virtual inheritance
public:
int b;
virtual void w();
};
class C : public A { //non-virtual inheritance
public:
int c;
virtual void x();
};
class D: public B, public C {
public:
int d;
virtual void y();
};
The sizeof A is 8 bytes -------------- 4(int a) + 4 (vptr) = 8
The sizeof B is 16 bytes -------------- Without virtual it should be 4 + 4 + 4 = 12. why there is another 4 bytes here? What's the layout of class B ?
The sizeof C is 12 bytes. -------------- 4 + 4 + 4 = 12. It's clear!
The sizeof D is 32 bytes -------------- it should be 16(class B) + 12(class C) + 4(int d) = 32. Is that right?
class A {
public:
int a;
virtual void v();
};
class B: virtual public A{ //virtual inheritance here
public:
int b;
virtual void w();
};
class C : virtual public A { //virtual inheritance here
public:
int c;
virtual void x();
};
class D: public B, public C {
public:
int d;
virtual void y();
};
sizeof A is 8
sizeof B is 16
sizeof C is 16
sizeof D is 28 Does it mean 28 = 16(class B) + 16(class C) - 8(class A) + 4 ( what's this? )
My question is , why there is an extra space when virtual inheritance is applied?
What's the underneath rule for the object size in this case?
What's the difference when virtual is applied on all the base classes and on part of the base classes?
This is all implementation defined. I'm using VC10 Beta2. The key to help understanding this stuff (the implementation of virtual functions), you need to know about a secret switch in the Visual Studio compiler, /d1reportSingleClassLayoutXXX. I'll get to that in a second.
The basic rule is the vtable needs to be located at offset 0 for any pointer to an object. This implies multiple vtables for multiple inheritance.
Couple questions here, I'll start at the top:
Does it mean that only one vptr is there even both of class B and class A have virtual function? Why there is only one vptr?
This is how virtual functions work, you want the base class and derived class to share the same vtable pointer (pointing to the implementation in the derived class.
It seems that in this case, two vptrs are in the layout.....How does this happen? I think the two vptrs one is for class A and another is for class B....so there is no vptr for the virtual function of class C?
This is the layout of class C, as reported by /d1reportSingleClassLayoutC:
class C size(20):
+---
| +--- (base class A)
0 | | {vfptr}
4 | | a
| +---
| +--- (base class B)
8 | | {vfptr}
12 | | b
| +---
16 | c
+---
You are correct, there are two vtables, one for each base class. This is how it works in multiple inheritance; if the C* is casted to a B*, the pointer value gets adjusted by 8 bytes. A vtable still needs to be at offset 0 for virtual function calls to work.
The vtable in the above layout for class A is treated as class C's vtable (when called through a C*).
The sizeof B is 16 bytes -------------- Without virtual it should be 4 + 4 + 4 = 12. why there is another 4 bytes here? What's the layout of class B ?
This is the layout of class B in this example:
class B size(20):
+---
0 | {vfptr}
4 | {vbptr}
8 | b
+---
+--- (virtual base A)
12 | {vfptr}
16 | a
+---
As you can see, there is an extra pointer to handle virtual inheritance. Virtual inheritance is complicated.
The sizeof D is 32 bytes -------------- it should be 16(class B) + 12(class C) + 4(int d) = 32. Is that right?
No, 36 bytes. Same deal with the virtual inheritance. Layout of D in this example:
class D size(36):
+---
| +--- (base class B)
0 | | {vfptr}
4 | | {vbptr}
8 | | b
| +---
| +--- (base class C)
| | +--- (base class A)
12 | | | {vfptr}
16 | | | a
| | +---
20 | | c
| +---
24 | d
+---
+--- (virtual base A)
28 | {vfptr}
32 | a
+---
My question is , why there is an extra space when virtual inheritance is applied?
Virtual base class pointer, it's complicated. Base classes are "combined" in virtual inheritance. Instead of having a base class embedded into a class, the class will have a pointer to the base class object in the layout. If you have two base classes using virtual inheritance (the "diamond" class hierarchy), they will both point to the same virtual base class in the object, instead of having a separate copy of that base class.
What's the underneath rule for the object size in this case?
Important point; there are no rules: the compiler can do whatever it needs to do.
And a final detail; to make all these class layout diagrams I am compiling with:
cl test.cpp /d1reportSingleClassLayoutXXX
Where XXX is a substring match of the structs/classes you want to see the layout of. Using this you can explore the affects of various inheritance schemes yourself, as well as why/where padding is added, etc.
Quote> My question is, what's the rule about the number of vptrs in inheritance?
There are no rulez, every compiler vendor is allowed to implement the semantics of inheritance the way he sees fit.
class B: public A {}, size = 12. That's pretty normal, one vtable for B that has both virtual methods, vtable pointer + 2*int = 12
class C : public A, public B {}, size = 20. C can arbitrarily extend the vtable of either A or B. 2*vtable pointer + 3*int = 20
Virtual inheritance: that's where you really hit the edges of undocumented behavior. For example, in MSVC the #pragma vtordisp and /vd compile options become relevant. There's some background info in this article. I studied this a few times and decided the compile option acronym was representative for what could happen to my code if I ever used it.
A good way to think about it is to understand what has to be done to handle up-casts. I'll try to answer your questions by showing the memory layout of objects of the classes you describe.
Code sample #2
The memory layout is as follows:
vptr | A::a | B::b
Upcasting a pointer to B to type A will result in the same address, with the same vptr being used. This is why there's no need for additional vptr's here.
Code sample #3
vptr | A::a | vptr | B::b | C::c
As you can see, there are two vptr's here, just like you guessed. Why? Because it's true that if we upcast from C to A we don't need to modify the address, and thus can use the same vptr. But if we upcast from C to B we do need that modification, and correspondingly we need a vptr at the start of the resulting object.
So, any inherited class beyond the first will require an additional vptr (unless that inherited class has no virtual methods, in which case it has no vptr).
Code sample #4 and beyond
When you derive virtually, you need a new pointer, called a base pointer, to point to the location in the memory layout of the derived classes. There can be more than one base pointer, of course.
So how does the memory layout look? That depends on the compiler. In your compiler it's probably something like
vptr | base pointer | B::b | vptr | A::a | C::c | vptr | A::a
\-----------------------------------------^
But other compilers may incorporate base pointers in the virtual table (by using offsets - that deserves another question).
You need a base pointer because when you derive in a virtual fashion, the derived class will appear only once in the memory layout (it may appear additional times if it's also derived normally, as in your example), so all its children must point to the exact same location.
EDIT: clarification - it all really depends on the compiler, the memory layout I showed can be different in different compilers.
All of this is completely implementation defined you realize. You can't count on any of it. There is no 'rule'.
In the inheritance example, here is how the virtual table for classes A and B might look:
class A
+-----------------+
| pointer to A::v |
+-----------------+
class B
+-----------------+
| pointer to A::v |
+-----------------+
| pointer to B::w |
+-----------------+
As you can see, if you have a pointer to class B's virtual table, it is also perfectly valid as class A's virtual table.
In your class C example, if you think about it, there is no way to make a virtual table that is both valid as a table for class C, class A, and class B. So the compiler makes two. One virtual table is valid for class A and C (mostly likely) and the other is valid for class A and B.
This obviously depends on the compiler implementation.
Anyway I think that I can sum up the following rules from the implementation given by a classic paper linked below and which gives the number of bytes you get in your examples (except for class D which would be 36 bytes and not 32!!!):
The size of an object of class T is:
The size of its fields PLUS the sum of the size of every object from which T inherits PLUS 4 bytes for every object from which T virtually inherits PLUS 4 bytes ONLY IF T needs ANOTHER v-table
Pay attention: if a class K is virtually inherited multiple times (at any level) you have to add the size of K only once
So we have to answer another question: When does a class need ANOTHER v-table?
A class that does not inherit from other classes needs a v-table only if it has one or more virtual methods
OTHERWISE, a class needs another v-table ONLY IF NONE of the classes from which it non virtually inherits does have a v-table
The End of the rules (which I think can be applied to match what Terry Mahaffey has explained in his answer) :)
Anyway my suggestion is to read the following paper by Bjarne Stroustrup (the creator of C++) which explains exactly these things: how many virtual tables are needed with virtual or non virtual inheritance... and why!
It's really a good reading:
http://www.hpc.unimelb.edu.au/nec/g1af05e/chap5.html
I am not sure but I think that it is because of pointer to Virtual method table

Can this change in subclass require recompilation of code dependent on superclass?

I have been learning some more "indepth" things about virtual tables recently and this question came to my mind.
Suppose we have this sample:
class A {
virtual void foo();
}
class B : public A {
void foo();
}
In this case from what I know there will be a vtable present for each class and the dispatch would be quite simple.
Now suppose we change the B class to something like this:
class B : public C, public A {
void foo();
}
If the class C has some virtual methods the dispatch mechanism for B will be more complicated. There will probably be 2 vtables for both inheritance paths B-C, B-A etc.
From what I've learned so far it seems that if there would be somewhere else in the codebase function like this:
void bar(A * a) {
a->foo();
}
It would need to compile now with the more complicated dispatch mechanism because at compile time we do not know if "a" is pointer to A or B.
Now to the question. Suppose we added the new class B to our codebase. It doesn't seem likely to me that it would require to recompile the code everywhere where the pointer to A is used.
From what I know the vtables are created by the compiler. However is it possible that this fixup is solved by the linker possibly during relocation? It does seem likely to me I just can not find any evidence to be sure and therefore go to sleep right now :)
Inside void bar(A * a), the pointer is definitely to an A object. That A object may be a subobject of something else like a B, but that's irrelevant. The A is self-contained and has its own vtable pointer which links to foo.
When the conversion from B * to A * occurs, such as when bar is called with a B *, a constant offset may be added to the B * to make it point to the A subobject. If the first thing in every object is the vtable, then this will also set the pointer to the A vtable as well. For single inheritance, the adjustment is unnecessary.
Here is what memory looks like for a typical implementation:
| ptr to B vt | members of C | members of B | ptr to AB vt | members of A |
B vt: | ptrs to methods of C (with B overrides) | ptrs to methods of B |
AB vt: | ptrs to methods of A (with B overrides) |
(Note that typically the AB vt is really still part of the B vt; they would be contiguous in memory. And ptrs to methods of B could then go after ptrs to methods of A. I just wrote it this way for formatting clarity.)
When you convert a B * to an A *, you go from this:
| ptr to B vt | members of C | members of B | ptr to AB vt | members of A |
^ your B * pointer value
to this:
| ptr to B vt | members of C | members of B | ptr to AB vt | members of A |
^ your A * pointer value
Using a static_cast from A * to B * will move the pointer backwards, in the other direction.
No, there's no need to recompile the code that depends only on A.
This is actually immediately follows from the principle of "independent translation", which typical modern C++ compilers adhere to. You can write all code dependent only on A in such a way, that it will never include any definitions that mention B. That means that any changes in B will not trigger the recompilation of any A-specific code. That in turn means that a C++ compiler that follows the principle of "independent translation" has to implement simple inheritance, multiple inheritance, virtual dispatch, hierarchical conversions etc. in such a way that any changes in B-specific code do not require recompilation of any A-specific code.
If that wasn't the case, the architecture of a typical C++ compiler would have to be significantly different.

Is vptr ever located not at start of object?

According to MSDN, __RTDynamicCast() function is used to implement dynamic_cast in Visual C++. One of its parameters is LONG VfDelta that is described as "offset of virtual function pointer in object".
AFAIK the vptr is always located at start of object, so offset will always be zero. I've looked closely at disassembly of various code snippets using dynamic_cast and I've never seen anything but zero being passed in place of this parameter.
Is vptr ever located anywhere but the object start? Can this offset be anything but zero?
In case of multiple inheritance there are more then one vptr and you need the offset. Take a look here: http://hacksoflife.blogspot.com/2007/02/c-objects-part-3-multiple-inheritance.html
I do not know what Microsoft does, but it's not always true that the vtable pointer is located at offset zero. An example of cases where it may not be is for multiple inheritance (especially if virtual base classes are involved).
Edit:
I'll expand this a bit with examples.
If the first base or a class does not have a vtbl, the derived class will not have a vtbl pointer at offset 0 (such inheritance is bad practice, but is permitted by the language).
If there is a virtual base, the derived class will generally have a pointer to the virtual base at offset 0, not a vtbl pointer.
This functionality is used when virtual inheritance exits ( think about the diamond inheritance chart ). This offset is the offset of the class itself inside the object.
If B and C derives from A, and D derives from both.
A
/ \
B C
\ /
D
Then B and C could be in either order in D. This is where the offset comes into action. So when you dynamic_cast an object of type A to type B, it might be different depending on wether the instance is of type B or D.
Finally to illustrate, here is possible layout of different class
Class B: Class C: class D:
| A | | A | | A |
| B | | C | | C |
| B |
| D |
In this case the offset of virtual function table of B can be either in 0 ( B instance case ), or sizeof( A ) + sizeof( C ) ( D instance case )

C++ object size with virtual methods

I have some questions about the object size with virtual.
1) virtual function
class A {
public:
int a;
virtual void v();
}
The size of class A is 8bytes....one integer(4 bytes) plus one virtual pointer(4 bytes)
It's clear!
class B: public A{
public:
int b;
virtual void w();
}
What's the size of class B? I tested using sizeof B, it prints
12
Does it mean that only one vptr is there even both of class B and class A have virtual function? Why there is only one vptr?
class A {
public:
int a;
virtual void v();
};
class B {
public:
int b;
virtual void w();
};
class C : public A, public B {
public:
int c;
virtual void x();
};
The sizeof C is 20........
It seems that in this case, two vptrs are in the layout.....How does this happen? I think the two vptrs one is for class A and another is for class B....so there is no vptr for the virtual function of class C?
My question is, what's the rule about the number of vptrs in inheritance?
2) virtual inheritance
class A {
public:
int a;
virtual void v();
};
class B: virtual public A{ //virtual inheritance
public:
int b;
virtual void w();
};
class C : public A { //non-virtual inheritance
public:
int c;
virtual void x();
};
class D: public B, public C {
public:
int d;
virtual void y();
};
The sizeof A is 8 bytes -------------- 4(int a) + 4 (vptr) = 8
The sizeof B is 16 bytes -------------- Without virtual it should be 4 + 4 + 4 = 12. why there is another 4 bytes here? What's the layout of class B ?
The sizeof C is 12 bytes. -------------- 4 + 4 + 4 = 12. It's clear!
The sizeof D is 32 bytes -------------- it should be 16(class B) + 12(class C) + 4(int d) = 32. Is that right?
class A {
public:
int a;
virtual void v();
};
class B: virtual public A{ //virtual inheritance here
public:
int b;
virtual void w();
};
class C : virtual public A { //virtual inheritance here
public:
int c;
virtual void x();
};
class D: public B, public C {
public:
int d;
virtual void y();
};
sizeof A is 8
sizeof B is 16
sizeof C is 16
sizeof D is 28 Does it mean 28 = 16(class B) + 16(class C) - 8(class A) + 4 ( what's this? )
My question is , why there is an extra space when virtual inheritance is applied?
What's the underneath rule for the object size in this case?
What's the difference when virtual is applied on all the base classes and on part of the base classes?
This is all implementation defined. I'm using VC10 Beta2. The key to help understanding this stuff (the implementation of virtual functions), you need to know about a secret switch in the Visual Studio compiler, /d1reportSingleClassLayoutXXX. I'll get to that in a second.
The basic rule is the vtable needs to be located at offset 0 for any pointer to an object. This implies multiple vtables for multiple inheritance.
Couple questions here, I'll start at the top:
Does it mean that only one vptr is there even both of class B and class A have virtual function? Why there is only one vptr?
This is how virtual functions work, you want the base class and derived class to share the same vtable pointer (pointing to the implementation in the derived class.
It seems that in this case, two vptrs are in the layout.....How does this happen? I think the two vptrs one is for class A and another is for class B....so there is no vptr for the virtual function of class C?
This is the layout of class C, as reported by /d1reportSingleClassLayoutC:
class C size(20):
+---
| +--- (base class A)
0 | | {vfptr}
4 | | a
| +---
| +--- (base class B)
8 | | {vfptr}
12 | | b
| +---
16 | c
+---
You are correct, there are two vtables, one for each base class. This is how it works in multiple inheritance; if the C* is casted to a B*, the pointer value gets adjusted by 8 bytes. A vtable still needs to be at offset 0 for virtual function calls to work.
The vtable in the above layout for class A is treated as class C's vtable (when called through a C*).
The sizeof B is 16 bytes -------------- Without virtual it should be 4 + 4 + 4 = 12. why there is another 4 bytes here? What's the layout of class B ?
This is the layout of class B in this example:
class B size(20):
+---
0 | {vfptr}
4 | {vbptr}
8 | b
+---
+--- (virtual base A)
12 | {vfptr}
16 | a
+---
As you can see, there is an extra pointer to handle virtual inheritance. Virtual inheritance is complicated.
The sizeof D is 32 bytes -------------- it should be 16(class B) + 12(class C) + 4(int d) = 32. Is that right?
No, 36 bytes. Same deal with the virtual inheritance. Layout of D in this example:
class D size(36):
+---
| +--- (base class B)
0 | | {vfptr}
4 | | {vbptr}
8 | | b
| +---
| +--- (base class C)
| | +--- (base class A)
12 | | | {vfptr}
16 | | | a
| | +---
20 | | c
| +---
24 | d
+---
+--- (virtual base A)
28 | {vfptr}
32 | a
+---
My question is , why there is an extra space when virtual inheritance is applied?
Virtual base class pointer, it's complicated. Base classes are "combined" in virtual inheritance. Instead of having a base class embedded into a class, the class will have a pointer to the base class object in the layout. If you have two base classes using virtual inheritance (the "diamond" class hierarchy), they will both point to the same virtual base class in the object, instead of having a separate copy of that base class.
What's the underneath rule for the object size in this case?
Important point; there are no rules: the compiler can do whatever it needs to do.
And a final detail; to make all these class layout diagrams I am compiling with:
cl test.cpp /d1reportSingleClassLayoutXXX
Where XXX is a substring match of the structs/classes you want to see the layout of. Using this you can explore the affects of various inheritance schemes yourself, as well as why/where padding is added, etc.
Quote> My question is, what's the rule about the number of vptrs in inheritance?
There are no rulez, every compiler vendor is allowed to implement the semantics of inheritance the way he sees fit.
class B: public A {}, size = 12. That's pretty normal, one vtable for B that has both virtual methods, vtable pointer + 2*int = 12
class C : public A, public B {}, size = 20. C can arbitrarily extend the vtable of either A or B. 2*vtable pointer + 3*int = 20
Virtual inheritance: that's where you really hit the edges of undocumented behavior. For example, in MSVC the #pragma vtordisp and /vd compile options become relevant. There's some background info in this article. I studied this a few times and decided the compile option acronym was representative for what could happen to my code if I ever used it.
A good way to think about it is to understand what has to be done to handle up-casts. I'll try to answer your questions by showing the memory layout of objects of the classes you describe.
Code sample #2
The memory layout is as follows:
vptr | A::a | B::b
Upcasting a pointer to B to type A will result in the same address, with the same vptr being used. This is why there's no need for additional vptr's here.
Code sample #3
vptr | A::a | vptr | B::b | C::c
As you can see, there are two vptr's here, just like you guessed. Why? Because it's true that if we upcast from C to A we don't need to modify the address, and thus can use the same vptr. But if we upcast from C to B we do need that modification, and correspondingly we need a vptr at the start of the resulting object.
So, any inherited class beyond the first will require an additional vptr (unless that inherited class has no virtual methods, in which case it has no vptr).
Code sample #4 and beyond
When you derive virtually, you need a new pointer, called a base pointer, to point to the location in the memory layout of the derived classes. There can be more than one base pointer, of course.
So how does the memory layout look? That depends on the compiler. In your compiler it's probably something like
vptr | base pointer | B::b | vptr | A::a | C::c | vptr | A::a
\-----------------------------------------^
But other compilers may incorporate base pointers in the virtual table (by using offsets - that deserves another question).
You need a base pointer because when you derive in a virtual fashion, the derived class will appear only once in the memory layout (it may appear additional times if it's also derived normally, as in your example), so all its children must point to the exact same location.
EDIT: clarification - it all really depends on the compiler, the memory layout I showed can be different in different compilers.
All of this is completely implementation defined you realize. You can't count on any of it. There is no 'rule'.
In the inheritance example, here is how the virtual table for classes A and B might look:
class A
+-----------------+
| pointer to A::v |
+-----------------+
class B
+-----------------+
| pointer to A::v |
+-----------------+
| pointer to B::w |
+-----------------+
As you can see, if you have a pointer to class B's virtual table, it is also perfectly valid as class A's virtual table.
In your class C example, if you think about it, there is no way to make a virtual table that is both valid as a table for class C, class A, and class B. So the compiler makes two. One virtual table is valid for class A and C (mostly likely) and the other is valid for class A and B.
This obviously depends on the compiler implementation.
Anyway I think that I can sum up the following rules from the implementation given by a classic paper linked below and which gives the number of bytes you get in your examples (except for class D which would be 36 bytes and not 32!!!):
The size of an object of class T is:
The size of its fields PLUS the sum of the size of every object from which T inherits PLUS 4 bytes for every object from which T virtually inherits PLUS 4 bytes ONLY IF T needs ANOTHER v-table
Pay attention: if a class K is virtually inherited multiple times (at any level) you have to add the size of K only once
So we have to answer another question: When does a class need ANOTHER v-table?
A class that does not inherit from other classes needs a v-table only if it has one or more virtual methods
OTHERWISE, a class needs another v-table ONLY IF NONE of the classes from which it non virtually inherits does have a v-table
The End of the rules (which I think can be applied to match what Terry Mahaffey has explained in his answer) :)
Anyway my suggestion is to read the following paper by Bjarne Stroustrup (the creator of C++) which explains exactly these things: how many virtual tables are needed with virtual or non virtual inheritance... and why!
It's really a good reading:
http://www.hpc.unimelb.edu.au/nec/g1af05e/chap5.html
I am not sure but I think that it is because of pointer to Virtual method table