15多重继承虚继承的内存布局.docx

上传人:b****4 文档编号:3487439 上传时间:2022-11-23 格式:DOCX 页数:17 大小:340.22KB
下载 相关 举报
15多重继承虚继承的内存布局.docx_第1页
第1页 / 共17页
15多重继承虚继承的内存布局.docx_第2页
第2页 / 共17页
15多重继承虚继承的内存布局.docx_第3页
第3页 / 共17页
15多重继承虚继承的内存布局.docx_第4页
第4页 / 共17页
15多重继承虚继承的内存布局.docx_第5页
第5页 / 共17页
点击查看更多>>
下载资源
资源描述

15多重继承虚继承的内存布局.docx

《15多重继承虚继承的内存布局.docx》由会员分享,可在线阅读,更多相关《15多重继承虚继承的内存布局.docx(17页珍藏版)》请在冰豆网上搜索。

15多重继承虚继承的内存布局.docx

15多重继承虚继承的内存布局

1.多重继承、虚继承的内存空间布局

对多重继承、虚继承的内存空间布局进行研究,循序渐进的进行处理,主要关注以下几点:

1)偏移表

2)虚表

3)数据成员

4)它们的位置

5)它们的大小及内容

6)它们间的关系。

1.1单继承,无虚函数的情况

单继承、无虚函数的情况是:

1)基类的数据成员

2)派生类新增的数据成员

派生类的大小是基类数据成员和派生类新增数据成员大小之和。

顺序是按照上面的基类、派生类的顺序进行布局。

1.2单继承,有虚函数的情况

单继承、有虚函数的情况:

1)派生类的虚表指针

2)基类的数据成员

3)派生类新增的数据成员

其中,派生类的虚表,是在基类的虚表基础之上所作的修改,有可能是:

1)对基类中虚函数地址的覆盖

2)派生类中新增的虚函数地址

1)只要有虚函数,就有虚表产生。

2)虚表中条目的个数,是本类中虚函数的个数

3)虚表中各条目的顺序,与类中声明(定义)的虚函数顺序一致

 

1.3多重继承,无虚函数的情况

多重继承、无虚函数的情况是:

1)基类的数据成员

2)基类的数据成员

3)派生类新增的数据成员

这里与1.1单继承,无虚函数的情况的差别是——可能存在多个基类。

这里基类数据成员的排放,是按照继承的数据依次进行的。

1.4多重继承,有虚函数的情况

多重继承,有虚函数的情况是:

1)基类的虚表指针

2)基类的数据成员

3)基类的虚表指针

4)基类的数据成员

5)派生类新增的数据成员

这里与1.2单继承,有虚函数的情况的差别是——虚表

这里说基类的虚表指针,其实是不太恰当的,因为它们实际上是派生类虚表的一部分。

也就说,派生类的虚表是由多个基类的虚表所构成的。

不存在一个单一的派生类的虚表。

派生类的虚表条目是在各基类的虚表基础之上修改所得,可能包括:

1)对基类中虚函数的覆盖,会更新各基类虚表中的条目

2)派生类中新增的虚函数地址,会追加到第一个继承的基类的虚表中

 

至此,上面

1.1单继承,无虚函数的情况

1.2单继承,有虚函数的情况

1.3多重继承,无虚函数的情况

1.4多重继承,有虚函数的情况

是从单继承/多重继承,无/有虚函数的角度进行的梳理。

下面将以菱形继承为主线,来进行梳理。

(菱形继承中可能出现二义性,会逐步的引入虚继承,虚基类的概念)

菱形继承(diamond-inheritance)

1.5菱形继承,无虚函数的情况

ClassA{};

ClassB:

publicA{};

ClassC:

publicA{};

ClassD:

publicB,publicC{};

菱形继承,无虚函数的情况是:

1)基类B的数据成员

a)基类A的数据成员

b)派生类B新增的数据成员

2)基类C的数据成员

a)基类A的数据成员

b)派生类C新增的数据成员

3)派生类D新增的数据成员

这里仍然是没有太大的变化,按照基类、派生类的顺序安放数据成员。

1.6菱形继承,有虚函数的情况

ClassA{};

ClassB:

publicA{};

ClassC:

publicA{};

ClassD:

publicB,publicC{};

菱形继承,有虚函数的情况:

1)基类B的虚表指针

a)基类A的虚函数(未被覆盖的部分)

b)基类B的虚函数(覆盖A的部分,新增的部分)

c)派生类D的虚函数(新增的部分)

2)基类B的数据成员

a)基类A的数据成员

b)派生类B新增的数据成员

3)基类C的虚表指针

a)基类A的函数(未被覆盖的部分)

b)基类C的虚函数(覆盖A的部分,新增的部分

4)基类C的数据成员

a)基类A的数据成员

b)派生类C新增的数据成员

5)派生类D新增的数据成员

仍然要说一点,这里说基类的虚表指针,其实是不太合适的,它们是派生类的虚表的一部分,是派生类在基类的虚表基础之上所做修改而来的:

1)如果派生类中的虚函数与基类中的形成覆盖,则派生类会对基类的虚表中相应条目做覆盖处理

2)派生类中新增的虚函数地址,追加至第一个继承的基类虚表中。

1.7菱形继承,无虚函数,为虚继承的情况

 

在上面的

1.5菱形继承,无虚函数的情况

1.6菱形继承,有虚函数的情况

中,最基类A,在内存空间中有多份拷贝。

利用虚继承可以解决,此时最基类A成为虚基类。

所以,菱形继承,无虚函数,为虚继承的情况,也就是菱形继承,无虚函数,有虚基类的情况。

虚继承的引入,使得虚基类在内存中仅存一份拷贝,同时带来的影响还有内存空间布局的变化。

大概有:

1)虚基类的数据成员在内存中的位置

2)偏移表

偏移表的存在,是因为——虚基类的单份存在,而虚基类A又被B,C所共享,所以对B,C而言,它们就各自需要确定A的所在位置。

偏移表就是用于该问题。

偏移表的数目,就是直接继承自虚基类的派生类的数目。

 

现在来一一测试。

在看到这些信息后,我们猜测其内存空间的布局:

1)B的偏移表,在ecx处

2)B的数据成员,在ecx+4处

3)C的偏移表,在ecx+8处

4)C的数据成员,在ecx+0C处

5)D的数据成员,在ecx+10处

6)A的数据成员,在ecx+14处

下面先对偏移表进行跟踪

正是通过这些入栈操作,来进行条件跳转的。

这是最后的内存空间布局。

现总结如下:

1)基类B的偏移表指针

2)基类B新增的数据成员

3)基类C的偏移表指针

4)基类C新增的数据成员

5)派生类D新增的数据成员

6)虚基类的数据成员

1.8菱形继承,有虚函数,为虚继承的情况

相较于1.7,这里增加了虚函数,那么又有什么不同呢?

根据这些,大概猜测其内存空间布局如下:

1)基类B的虚表指针

2)基类B的偏移表指针

3)基类B的数据成员

4)基类C的虚表指针

5)基类C的偏移表指针

6)基类C的数据成员

7)派生类D的虚表指针(后证实,不是这样的,而是分割)

8)派生类D的数据成员

9)虚基类A的虚表

10)虚基类A的数据成员

下面来一一查看。

设置偏移表。

偏移表的设置,在虚表设置之前。

这里的偏移表的第二项,用于确定本类(B)对虚基类(A)的定位。

而第一项,像是本类的虚表指针相对于偏移表的偏移。

这里有分割线的概念,用于分割非虚基类和虚基类。

此时,对于两个虚表,有点疑惑

至此,完成了对内存空间布局的更新,现总结如下:

1)基类B的虚表指针

a)B新增的虚函数

b)D新增的虚函数

2)基类B的偏移表指针

3)基类B新增的数据成员

4)基类C的虚表指针

a)C新增的虚函数

5)基类C的偏移表指针

6)基类C新增的数据成员

7)派生类D新增的数据成员

8)分割

9)虚基类的虚表指针

a)A未被覆盖的虚函数

b)D覆盖的虚函数

10)虚基类的数据成员

所以,这里各虚表的特点是——仅存放新增的虚函数地址。

至于那些覆盖的,则放在虚基类的虚表中。

上面这些,

1.5菱形继承,无虚函数的情况

1.6菱形继承,有虚函数的情况

1.7菱形继承,无虚函数,为虚继承的情况

1.8菱形继承,有虚函数,为虚继承的情况

是以菱形继承为基础,控制有无虚函数,是否为虚继承,所进行的测试。

2.总结

现在来试着从更全面的角度来看,试图总结它们的规律。

2.1无虚函数,仅有数据成员的情况

1.1单继承,无虚函数的情况

1.3多重继承,无虚函数的情况

1.5菱形继承,无虚函数的情况

1.7菱形继承,无虚函数,为虚继承的情况

1.1,1.3,1.5的布局都很相似——基类数据成员、派生类新增的数据成员

按照这样的顺序进行排放。

而在1.7的情境中,因虚基类的存在,仅存一份拷贝,引入偏移表。

2.2有虚函数的情况

这是

1.2单继承,有虚函数的情况

1.4多重继承,有虚函数的情况

1.6菱形继承,有虚函数的情况

1.8菱形继承,有虚函数,为虚继承的情况

虚函数的存在,引入了虚表。

可见它们也大致遵循着类似的规则:

1)虚表指针,偏移表指针,数据成员

2)原数据成员,新增数据成员

3)原虚函数,新增的虚函数

2.3其他情况

上面大致描述了一些基本框架情况,在此基础上还可以有其他的变形。

比如:

1)多重继承中,A,B——>CA没有虚函数,B有虚函数

这对内存空间布局的影响

B的虚表指针,

B的数据成员,

A的数据成员,

C新增的数据成员

2)单单两个类间的虚继承

A——>virtualB

a)无虚函数的情况

B的偏移表指针

B新增的数据成员

虚基类A的数据成员

b)有虚函数的情况

这还要看是否发生覆盖,如果没有覆盖:

B的偏移表指针

B新增的数据成员

虚基类A的虚表指针

虚基类A的数据成员

如果有了覆盖:

B的偏移指针

B新增的数据成员

分割

虚基类A的虚表指针

虚基类A的数据成员

其他不再详述。

2.4覆盖、新增

在虚继承中,有分割这么一说——用0x00000000来分割非虚基类和虚基类。

但是,分割是否出现,这还取决于是否新增了虚函数。

其实,对于虚函数,都存在覆盖和新增的视角处理。

这涉及到对虚表的更新处理。

2.5本类的虚表

可以这么说,在继承中是不存在本类的虚表这么一说的。

都是在其基类的虚表基础之上,或进行覆盖,或进行新增。

当然了,一般的,新增的虚函数地址,是存放在第一个基类虚表里的。

2.6偏移表,虚表

偏移表中一般两项,第二项用于本类对虚基类的定位,是偏移相关。

而第一项,好像是本类的虚表相对于本类偏移表的偏移。

当本类没有虚表时,第一项就是0。

当本类的虚表在偏移之上时,该值为负,刚好是它们间的差值。

(一般如此)

为正的情况呢?

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 表格模板 > 合同协议

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1