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://pwwzsj.com/article/dijjjo.html