Analysis of virtual base class pointers and virtual base class tables when virtual inheritance and ordinary inheritance coexist in C++

Let’s first look at the virtual base class pointer and virtual base class table generated by virtual inheritance in C++, and the virtual function pointer and virtual function table generated by virtual function_Meng Xiaopang_H’s Blog-CSDN Blog_Virtual base class pointer and virtual function pointer

As we all know, C++ virtual inheritance will save a copy of the parent class of the parent class. This is implemented using virtual base class pointers and virtual base class tables. However, it is not clear what the situation will be when virtual inheritance and ordinary inheritance exist at the same time.

For example, the following example is familiar to everyone

one,

Son1 and Son2 inherit Base virtually, and Grandson inherits Son1 and Son2 normally.

You can see that there are Son1's virtual base class pointer vbptr and Son2's virtual base class pointer vbptr in Grandson. They jointly point to the Base class through the offset of their respective virtual base class tables, so when Grandson accesses Base through Son1 or Son2 Classes all access the same Base, which avoids the trouble of generating two Base classes.

class Base
{
public:
	int a;
};

class Son1 :virtual public Base
{
public:
	int a;
};

class Son2 :virtual public Base
{
public:
	int a;
};


class Grandson :public Son1, public Son2
{
public:
	int a;
};
1>class Grandson	size(24):
1>	+---
1> 0	| +--- (base class Son1)   
1> 0	| | {vbptr}
1> 4	| | a
1>	| +---
1> 8	| +--- (base class Son2)
1> 8	| | {vbptr}
1>12	| | a
1>	| +---
1>16	| a
1>	+---
1>	+--- (virtual base Base)
1>20	| a
1>	+---
1>Grandson::$vbtable@Son1@:
1> 0	| 0
1> 1	| 20 (Grandsond(Son1+0)Base)
1>Grandson::$vbtable@Son2@:
1> 0	| 0
1> 1	| 12 (Grandsond(Son2+0)Base)

two,

When Grandson virtually inherits Son1 and Son2, it is not difficult to guess that Grandson will also generate a vbptr pointer.

class Base
{
public:
	int a;
};

class Son1 :virtual public Base
{
public:
	int a;
};

class Son2 :virtual public Base
{
public:
	int a;
};


class Grandson :virtual public Son1, virtual public Son2
{
public:
	int a;
};
1>class Grandson	size(28):
1>	+---
1> 0	| {vbptr}
1> 4	| a
1>	+---
1>	+--- (virtual base Base)
1> 8	| a
1>	+---
1>	+--- (virtual base Son1)
1>12	| {vbptr}
1>16	| a
1>	+---
1>	+--- (virtual base Son2)
1>20	| {vbptr}
1>24	| a
1>	+---
1>Grandson::$vbtable@Grandson@:
1> 0	| 0
1> 1	| 8 (Grandsond(Grandson+0)Base)
1> 2	| 12 (Grandsond(Grandson+0)Son1)
1> 3	| 20 (Grandsond(Grandson+0)Son2)
1>Grandson::$vbtable@Son1@:
1> 0	| 0
1> 1	| -4 (Grandsond(Son1+0)Base)
1>Grandson::$vbtable@Son2@:
1> 0	| 0
1> 1	| -12 (Grandsond(Son2+0)Base)

Grandson did generate a virtual pointer vbptr pointing to its own virtual base class table. Since it virtually inherits Son1 and Son2, its virtual base class table points to Base, Son1, Son2 through the offset.

three,

So, what if Grandson inherits Son1 virtually and inherits Son2 normally? what will happen

class Base
{
public:
	int a;
};

class Son1 :virtual public Base
{
public:
	int a;
};

class Son2 :virtual public Base
{
public:
	int a;
};


class Grandson :virtual public Son1,  public Son2
{
public:
	int a;
};
1>class Grandson	size(24):
1>	+---
1> 0	| +--- (base class Son2)
1> 0	| | {vbptr}
1> 4	| | a
1>	| +---
1> 8	| a
1>	+---
1>	+--- (virtual base Base)
1>12	| a
1>	+---
1>	+--- (virtual base Son1)
1>16	| {vbptr}
1>20	| a
1>	+---
1>Grandson::$vbtable@Son2@:
1> 0	| 0
1> 1	| 12 (Grandsond(Son2+0)Base)
1> 2	| 16 (Grandsond(Grandson+0)Son1)
1>Grandson::$vbtable@Son1@:
1> 0	| 0
1> 1	| -4 (Grandsond(Son1+0)Base)

It can be magically discovered that Grandson does not have a virtual base class pointer, but the virtual base class table of Son2 actually stores the offsets pointing to Son1 and Base.

Four,

According to the above example, we can know

1. Virtual inheritance will definitely generate a virtual base class table that points to the virtual inherited class through the offset.

2. If there is not all virtual inheritance, then a class with ordinary inheritance but containing a virtual base class pointer will be found to do the above thing, because this can avoid the overhead of creating a virtual base class pointer (4 bytes)

The following example is a little more complicated

class Base
{
public:
	int a;
};

class Son1 :virtual public Base
{
public:
	int a;
};

class Son2 :virtual public Base
{
public:
	int a;
};

class Son3 :public Son1
{
public:
	int a;
};

class Son4 :public Son2
{
public:
	int a;
};

class Grandson :virtual public Son3,  public Son4
{
public:
	int a;
};
1>class Grandson	size(32):
1>	+---
1> 0	| +--- (base class Son4)
1> 0	| | +--- (base class Son2)
1> 0	| | | {vbptr}
1> 4	| | | a
1>	| | +---
1> 8	| | a
1>	| +---
1>12	| a
1>	+---
1>	+--- (virtual base Base)
1>16	| a
1>	+---
1>	+--- (virtual base Son3)
1>20	| +--- (base class Son1)
1>20	| | {vbptr}
1>24	| | a
1>	| +---
1>28	| a
1>	+---
1>Grandson::$vbtable@Son4@:
1> 0	| 0
1> 1	| 16 (Grandsond(Son2+0)Base)
1> 2	| 20 (Grandsond(Grandson+0)Son3)
1>Grandson::$vbtable@Son3@:
1> 0	| 0
1> 1	| -4 (Grandsond(Son1+0)Base)

 You can see that Grandson inherits Son3 virtually and inherits Son4 normally. Since Grandson virtually inherits Son3, there must be a virtual base class table to store the offset pointing to Son3. Son4 happens to be a normal inheritance, and Son2 inherited by Son4 has a virtual inheritance pointer, so Son4 takes on this task.

Remove the virtual in front of Son2 and see the changes

class Base
{
public:
	int a;
};

class Son1 :virtual public Base
{
public:
	int a;
};

class Son2 :public Base
{
public:
	int a;
};

class Son3 :public Son1
{
public:
	int a;
};

class Son4 :public Son2
{
public:
	int a;
};

class Grandson :virtual public Son3,  public Son4
{
public:
	int a;
};
1>class Grandson	size(36):
1>	+---
1> 0	| +--- (base class Son4)
1> 0	| | +--- (base class Son2)
1> 0	| | | +--- (base class Base)
1> 0	| | | | a
1>	| | | +---
1> 4	| | | a
1>	| | +---
1> 8	| | a
1>	| +---
1>12	| {vbptr}
1>16	| a
1>	+---
1>	+--- (virtual base Base)
1>20	| a
1>	+---
1>	+--- (virtual base Son3)
1>24	| +--- (base class Son1)
1>24	| | {vbptr}
1>28	| | a
1>	| +---
1>32	| a
1>	+---
1>Grandson::$vbtable@Grandson@:
1> 0	| -12
1> 1	| 8 (Grandsond(Grandson+12)Base)
1> 2	| 12 (Grandsond(Grandson+12)Son3)
1>Grandson::$vbtable@Son3@:
1> 0	| 0
1> 1	| -4 (Grandsond(Son1+0)Base)

 You can see that Grandson has its own virtual base class pointer and virtual base class table. This is because the ordinary inherited Son4 does not have a virtual base class pointer, so there is no other way. The system allocates a virtual base class pointer (size from 32 changed to 36) to complete the task of pointing to the classes inherited by each virtual inheritance

Summarize:

In general, as long as there is a virtual inheritance in the relationship chain during inheritance, then only one copy of it will be saved in subsequent inheritances. How to allocate the virtual base class table and virtual base class pointer depends on the system operation. Through less Virtual inheritance can also reduce the size of the class, but it seems to be of no use.

Guess you like

Origin blog.csdn.net/qq_30798083/article/details/128150821