C ++ virtual functions, virtual inheritance and explore its memory layout

Virtual function table pointer to the virtual function table layout

Consider the following class:

class A {
 public:
  int a;
  virtual void f1() {}
  virtual void f2() {}
};

int main() {
    A *a1 = new A();
  return 0;
}

First clear, sizeof (A) is the output of 16, because: class A contains an int is 4 bytes, and contains a virtual function, it must contain a pointer to the vtable vptr, and vptr 8 bytes = 8 + 4 12, 8 aligned to the boundary, i.e. 16

Virtual function table pointer vptr

To accomplish polymorphic functions, modern compilers C ++ using object model table driven, specifically, addresses of all virtual functions are stored in a table, and this table is called 虚函数表vtable, the virtual function table the address discharge is present in the class, called虚函数表指针vptr

Use clang derive the layout object class A, the following output:

*** Dumping AST Record Layout
         0 | class A
         0 |   (A vtable pointer)
         8 |   int a
           | [sizeof=16, dsize=12, align=8,
           |  nvsize=12, nvalign=8]

It can be seen in the layout of the object class A, the first one is vptr (8 bytes)

Vtable the virtual function table

Using the export function clang virtual function table, you can see the specific contents of the virtual function table of the class A as follows:

Vtable for 'A' (4 entries).
   0 | offset_to_top (0)
   1 | A RTTI
       -- (A, 0) vtable address --
   2 | void A::f1()
   3 | void A::f2()

VTable indices for 'A' (2 entries).
   0 | void A::f1()
   1 | void A::f2()

Note that: - (A, 0) vtable address - mean, vptr class A resulting object is pointing to this address, and after the contents of this address is the address of all virtual functions, that is to say vptr not to the beginning of the entire virtual function table (i.e. offset_to_top (0) at)

Virtual function tables only virtual function address? From the above output, it is clear not

A complete the vtable, has the following contents (contents of the virtual function table is referred to 条目, or 实体, not all additional entries will appear, but if there is, must appear in the following order):

  1. virtual call (vcall) offsets: for correcting the virtual function pointer
  2. virtual base (vbase) offsets: used to access the virtual bases of an object,
  3. offset to top: recording the address of this object's vtable offset into a top address of the object
  4. typeinfo pointer: for RTTI
  5. vitual function pointers: a series of virtual function pointer

Virtual function table layout for the single inheritance

Discussed below, the case of single inheritance, where the specific virtual function table in which various items, consider the following code:

class A {
 public:
  int a;
  virtual void f1() {}
  virtual void f2() {}
};

class B : public A {
 public:
  int b;
  void f1() override {}
};

int main() {
  A *a1 = new A();
  B *b1 = new B();
  return 0;
}

First, it is clear: the size sizeof (A) and sizeof (B) of:

  1. sizeof (A): 4 + 8 = 12, adjusted to the boundary 8, it is 16
  2. sizeof (B): 4 + 4 + 8 = 16, boundary alignment is not required, it is 16

A1 and b1 by using the layout object clang view of class A and class B produced, the following output:

*** Dumping AST Record Layout
         0 | class A
         0 |   (A vtable pointer)
         8 |   int a
           | [sizeof=16, dsize=12, align=8,
           |  nvsize=12, nvalign=8]

*** Dumping AST Record Layout
         0 | class B
         0 |   class A (primary base)
         0 |     (A vtable pointer)
         8 |     int a
        12 |   int b
           | [sizeof=16, dsize=16, align=8,
           |  nvsize=16, nvalign=8]

// 对于b1来说:在构造b1时,首先需要构造一个A父类对象,所以b1的布局最开始上半部分是一个A父类对象

See clang using class A and class B virtual function table of contents, the following output:

Vtable for 'A' (4 entries).
   0 | offset_to_top (0)
   1 | A RTTI
       -- (A, 0) vtable address --
   2 | void A::f1()
   3 | void A::f2()

VTable indices for 'A' (2 entries).
   0 | void A::f1()
   1 | void A::f2()

Vtable for 'B' (4 entries).
   0 | offset_to_top (0)
   1 | B RTTI
       -- (A, 0) vtable address --
       -- (B, 0) vtable address --
   2 | void B::f1()
   3 | void A::f2()

VTable indices for 'B' (1 entries).
   0 | void B::f1()

In the virtual function table of contents in class B, the following two:

-- (A, 0) vtable address --
-- (B, 0) vtable address --

meaning is:

  1. If a pointer or reference to look at an object of type A class B, then at this time point it is vptr - (A, 0) vtable address -
  2. If a pointer or reference to look at an object of type B class B, then at this time point it is vptr - (B, 0) vtable address -

Although in the above example, the two addresses are identical, which means the case where a single-stranded inheritance, dynamic down-conversion and up-conversion, without requiring any modifications to this address pointer, only need to re "interpretation"

( I should explain: a pointer or reference type, the real significance of the impact of the compiler how to interpret or how the compiler to treat a pointer or reference data points to memory)

The virtual function table entry contains a single succession is relatively small, it can be easily understood

Virtual function table layout in the case of multiple inheritance

l Consider the following code:

class A {
 public:
  int a;
  virtual void f1() {}
};

class B {
 public:
  int b;
  virtual void f2() {}
};

class C : public A, public B {
 public:
  int c;
  void f1() override {}
  void f2() override {}
};

int main() {
  A *a1 = new A();
  B *b1 = new B();
  C *c1 = new C();
    return 0;
}

First of all, still talk about A, B, C three class size:

  1. sizeof (A): 4 + 8 = 12, 8 to adjust to the boundary, i.e. 16
  2. sizeof (B): 4 + 8 = 12, 8 to adjust to the boundary, i.e. 16
  3. sizeof (C): 4 + 4 + 4 +8 + 8 = 28, 8 to adjust to the boundary, i.e. 32

There is a problem, why calculate the size of C, plus two 8? Because these two are the two vptr 8, then how will two C vptr it, would later explain, would not be discussed here

See class A, B, C of the three objects layout, as follows:

*** Dumping AST Record Layout
         0 | class A
         0 |   (A vtable pointer)
         8 |   int a
           | [sizeof=16, dsize=12, align=8,
           |  nvsize=12, nvalign=8]

*** Dumping AST Record Layout
         0 | class B
         0 |   (B vtable pointer)
         8 |   int b
           | [sizeof=16, dsize=12, align=8,
           |  nvsize=12, nvalign=8]

*** Dumping AST Record Layout
         0 | class C
         0 |   class A (primary base)
         0 |     (A vtable pointer)
         8 |     int a
        16 |   class B (base)
        16 |     (B vtable pointer)
        24 |     int b
        28 |   int c
           | [sizeof=32, dsize=32, align=8,
           |  nvsize=32, nvalign=8]

All entries See class A, B, C of the virtual function table:

Vtable for 'A' (3 entries).
   0 | offset_to_top (0)
   1 | A RTTI
       -- (A, 0) vtable address --
   2 | void A::f1()

VTable indices for 'A' (1 entries).
   0 | void A::f1()

Vtable for 'B' (3 entries).
   0 | offset_to_top (0)
   1 | B RTTI
       -- (B, 0) vtable address --
   2 | void B::f2()

VTable indices for 'B' (1 entries).
   0 | void B::f2()

Vtable for 'C' (7 entries).
   0 | offset_to_top (0)
   1 | C RTTI
       -- (A, 0) vtable address --
       -- (C, 0) vtable address --
   2 | void C::f1()
   3 | void C::f2()
   4 | offset_to_top (-16)
   5 | C RTTI
       -- (B, 16) vtable address --
   6 | void C::f2()
       [this adjustment: -16 non-virtual]

Thunks for 'void C::f2()' (1 entry).
   0 | this adjustment: -16 non-virtual

VTable indices for 'C' (2 entries).
   0 | void C::f1()
   1 | void C::f2()

At this point you can see, multiple inheritance, virtual function table a lot more than a single inheritance is no entry, then careful discussion

Why C 1 layout in two vptr?

Different single-stranded inheritance, since A and B are completely independent, no virtual functions thereof sequential relationship, i.e., f1 and f2 have the same imaginary table offset start position, it can not be offset in the order of arrangement; and member variables a and B are independent, and therefore does not have a base class comprising inter relation; this makes the C a and B must be in the region of two disjoint, and the need to have two virtual pointers are they virtual function table index

Memory layout primary base 2 class C object is what meaning?

Watch again at the memory layout of objects of class C:

*** Dumping AST Record Layout
         0 | class C
         0 |   class A (primary base)
         0 |     (A vtable pointer)
         8 |     int a
        16 |   class B (base)
        16 |     (B vtable pointer)
        24 |     int b
        28 |   int c
           | [sizeof=32, dsize=32, align=8,
           |  nvsize=32, nvalign=8]

Class C is a public already knows the way inherited class A and class B, and class A is marked primary base, its meaning is: class C to a class A 主基类, class C is the virtual function 并入virtual function table among class A

Case 3 under multiple inheritance, virtual function tables vtable of the class C features?

Multiple inheritance, the virtual function table of contents class C is as follows:

Vtable for 'C' (7 entries).
   0 | offset_to_top (0)
   1 | C RTTI
       -- (A, 0) vtable address --
       -- (C, 0) vtable address --
   2 | void C::f1()
   3 | void C::f2()
   4 | offset_to_top (-16)
   5 | C RTTI
       -- (B, 16) vtable address --
   6 | void C::f2()
       [this adjustment: -16 non-virtual]

Thunks for 'void C::f2()' (1 entry).
   0 | this adjustment: -16 non-virtual

VTable indices for 'C' (2 entries).
   0 | void C::f1()
   1 | void C::f2()

We can see the entire virtual function table is actually a class C 两个虚函数表拼接而成(which also corresponds to the class C Why two vptr)

A step by step analysis, look at the virtual function table of the upper part:

   0 | offset_to_top (0)
   1 | C RTTI
       -- (A, 0) vtable address --
       -- (C, 0) vtable address --
   2 | void C::f1()
   3 | void C::f2()

As already mentioned, class C will as a class A 主基类, and to their virtual functions incorporated into the virtual function table of class A, it is possible to see above the content will

Therefore, class C in a vptr that points to the virtual function table

Look at the lower half of the virtual function table:

   4 | offset_to_top (-16)
   5 | C RTTI
       -- (B, 16) vtable address --
   6 | void C::f2()
       [this adjustment: -16 non-virtual]

Thunks for 'void C::f2()' (1 entry).
   0 | this adjustment: -16 non-virtual

Note that, in this case the offset is already offset_to_top in a 16

I said before, meaning offset_to_top is: the actual type of the object from the current type conversion for the object address offset

In the multiple inheritance, to class A, B, C as an example, class A and class B, and class C type of a pointer or a reference point may be instances of class C type, for example:

C cc = new C();
B &bb = cc;

bb.f1(); // 我们知道,由于多态,此时实际调用的class C中的虚函数f1(),即相当于cc.f1()
// 回顾class C的对象的内存布局
// 当我们用 B类型的引用接收cc对象时,this指针相当于指在了`16 |   class B (base)`这个地方,要想实现多态,需要将this指针向上偏移16个字节,这样this指针才能指向cc对象的起始地址,编译器才能以C类型来解释cc这个对象而不会出错
*** Dumping AST Record Layout
         0 | class C
         0 |   class A (primary base)
         0 |     (A vtable pointer)
         8 |     int a
        16 |   class B (base)
        16 |     (B vtable pointer)
        24 |     int b
        28 |   int c
           | [sizeof=32, dsize=32, align=8,
           |  nvsize=32, nvalign=8]

In the multiple inheritance, class due to the different origin of the base may be in a different position, so that when they need to be converted to the actual type, the this pointer offset is not the same, and due to the characteristics of the multi-state, the actual type of the cc compile time can not be determined; it necessarily requires a thing to help us determine the actual type cc at run time, this thing is offset_to_top. Let by this指针adding offset_to_topthe offset, so that this can be a pointer to the starting address of the actual type

class C lower half of the virtual function table there is a noteworthy:

6 | void C::f2()
       [this adjustment: -16 non-virtual]

Thunks for 'void C::f2()' (1 entry).
   0 | this adjustment: -16 non-virtual

It means that, when a pointer to a type B or class C is accepted reference object and call f2: this is necessary to adjust the pointer bytes -16, then call (which, like the above mentioned, this will be adjusted upward 16 bytes is to make this point to start address of the object's class C, which will be class C compiler to look at this this type pointer), and then call f2, will ensure that the call is a virtual function table of the class C their f2

Guess you like

Origin www.cnblogs.com/xhb19960928/p/11686268.html