C++虚继承和普通继承同时存在时虚基类指针和虚基类表的分析-创新互联

先看这个C++中虚继承产生的虚基类指针和虚基类表,虚函数产生的虚函数指针和虚函数表_孟小胖_H的博客-博客_虚基类指针和虚函数指针

专注于为中小企业提供成都网站设计、成都网站制作服务,电脑端+手机端+微信端的三站合一,更高效的管理,为中小企业东乡族免费做网站提供优质的服务。我们立足成都,凝聚了一批互联网行业人才,有力地推动了上1000家企业的稳健成长,帮助中小企业通过网站建设实现规模扩充和转变。

众所周知,C++虚继承时会保存父类的父类的一个副本,这是用虚基类指针和虚基类表实现的,但是虚继承和普通继承同时存在时情况如何就不是很清楚了。

比如下面的例子大家很熟悉

一、

Son1,Son2虚继承Base,Grandson普通继承Son1,Son2

可以看到Grandson中有Son1的虚基类指针vbptr和Son2的虚基类指针vbptr,它们通过各自的虚基类表的偏移量共同指向了Base类,所以当Grandson无论通过Son1还是Son2访问Base类时都是访问同一个Base,这就避免了产生两个Base类的麻烦

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)

二、

当Grandson都虚继承Son1,Son2时不难猜想Grandson也会产生一个vbptr指针

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的确产生了一个虚指针vbptr指向自己的虚基类表,由于虚继承了Son1,Son2,那么它的虚基类表通过偏移量指向了Base,Son1,Son2

三、

那么,如果Grandson虚继承Son1,普通继承Son2呢?情况会是怎么样

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)

可以神奇地发现,Grandson自己没有虚基类指针,但Son2的虚基类表竟然存放了指向Son1和Base的偏移量

四、

根据上面的例子可以得知

1.虚继承一定会产生有虚基类表通过偏移量去指向那个被虚继承的类

2.如果没有全部虚继承,那么会找一个普通继承但包含虚基类指针的类来做这个上面的事,因为这样能避免产生创建虚基类指针的开销(4个字节)

下面这个例子复杂一点

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)

可以看到Grandson虚继承了Son3,普通继承了Son4。既然Grandson虚继承了Son3,那么就得有一个虚基类表来存放指向Son3的偏移量,刚好Son4是普通继承,而Son4继承的Son2有一个虚继承指针,所以由Son4来承担了这个任务

将Son2前面的virtual去掉看看变化

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)

可以看到Grandson多了一个自己的虚基类指针和虚基类表,这是因为普通继承的Son4没有虚基类指针了,所以没办法,系统给Grandson分配了一个虚基类指针(大小从32变为了36)来完成指向各个虚继承来的类的任务

总结:

总的来说,只要在继承时关系链中有一次虚继承,那么之后的继承都只会保存其一个副本,而如何分配虚基类表和虚基类指针就得看系统操作了,通过少虚继承一个还可以减少类的大小,不过好像没啥用。

你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧


本文名称:C++虚继承和普通继承同时存在时虚基类指针和虚基类表的分析-创新互联
链接分享:http://myzitong.com/article/dijjjo.html