Understanding of vtable in derived classes
Asked Answered
T

1

7

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.

Timisoara answered 24/5, 2017 at 8:9 Comment(10)
why vptr in struct B need 8 bytes?Ellette
@walker I doubt it does. It's probably some padding.Radiotelegraphy
Possible duplicate of How many vptr will a object of class(uses single/multiple inheritance) have?Puklich
@walker it's padding to sizeof(double)Timisoara
This is all platform- and/or compiler-specific implementation detail. Unless you say what platform and compiler you care about (all I know so far is it seems to be 32-bit), the best you can expect is an essay on possible implementations (and that's probably too broad). Anyway, you can just inspect the assembly output to see what your compiler is doing.Sulfa
@Puklich B doesn't inherit ATimisoara
Why would you want another vptr?Townsley
@Sulfa Can you cite even one compiler that does it differently?Townsley
Yes: all 64-bit flat memory model platforms do it differently than your illustration above, because the pointer will be 8 bytes rather than 4.Sulfa
@Sulfa I mentioned about the platformTimisoara
N
5

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)

Nairobi answered 24/5, 2017 at 8:41 Comment(1)
Yes, it turned out to be just alignment, not a new vptr.Timisoara

© 2022 - 2024 — McMap. All rights reserved.