renzo
renzo

Reputation: 321

Understanding of vtable in derived classes

I'm trying to undestand some low-level things with virtual table and inheritance.

When you create new class by inheriting two classes and adding new virtual functions, where exactly the vptr will be stored?

It seems to me, that compiler performs some 'vptr-optimization' in that case. And I'm trying to figure it out.

Suppose, we have the following structs:

struct A
{
  int a;
  virtual void fa();
};
struct B
{
  double b;
  virtual void fb();
};
struct C : A, B
{
  char c;
  virtual void fa();
  virtual void fb();
  virtual void fc();
};

In case of x86 and align=4, A and B in memory will look like this:

   +------+------+
A: | vptr |  a   |
   +------+------+
sizeof(A) = 4 + 4 = 8

   +------+------+------+------+
B: | vptr        |      b      |
   +------+------+------+------+
sizeof(B) = 8 + 8 = 16

But when I try to reassemble C, I get this:

   +------+------+------+------+------+------+------+
C: | vptr |  a   | vptr        |      b      |  c   |
   +------+------+------+------+------+------+------+
but sizeof(C) = 32

С с;
(C*)&c; // 0x100
(B*)&c; // 0x108
(A*)&c; // 0x100
&c.a;   // 0x104
&c.b;   // 0x110
&c.c;   // 0x118

So where is the vptr of C? I can suppose that compiler merge different virtual tables (ex. vptr of A and C), but in that case why sizeof(C) returns sizeof(A) + sizeof(B) + sizeof(alligned_char) + sizeof(vptr)

The struct D : public C {} has the same story - there is no vptr of D.

The compiler I use is msvc 2012 x86.

Upvotes: 7

Views: 1491

Answers (1)

mksteve
mksteve

Reputation: 13073

The compiler has to juggle simplicity, with the fact that the base classes need to exist within the object.

+------+---------+----+
|   A  | B       |  C |
+------+---------+----+

So

  • A needs to exist as if it was not derived.
  • B needs to exist as if it was not derived.
  • C has new freedom.

virtual functions from A and B will be patched for the derived class C's implementation. C will add virtual functions to (probably) the existing first element As vtable.

base vtable for A

+------+
| A:fa |
+------+

vtable for A in the derived class C

+------+
| C:fa |   // implemented by derived class.
+------+
| C:fb |   // any calls to fb need to be sent to `C`'s implementation
+------+
| C:fc |   // any calls to fc can be overridden by vtable.
+------+ 

vtable for B in the derived class C

+------+
| C:fb |   // overridden, but no need to add fc, fa to this table.
+------+

I think alignment rules are causing the size of C to be padded so the alignment sensitive double is correctly aligned (ensure that an array of C is properly aligned).

The size of B is the size of the vptr (4) and padding to ensure the double is aligned (4) and the size of the double (8)

Upvotes: 5

Related Questions