赞
踩
目录
所谓虚继承(virtual)就是子类中只有一份间接父类的数据。该技术用于解决多继承中的父类为非虚基类时出现的数据冗余问题,即菱形继承问题。
小编用一张图来表述一下:
如果是下图这种非虚继承,那么D类中就会出现两个 int a,当我们用D实例化对象调用a时,编译会报错,因为发生了混淆。除非指定类域B或C。当然指定A也可以,因为默认会从第一个父类中找。
此时,D的实例化对象内部结构如下:
而当我们使用虚继承时,结构是下图这样,D中只有一份父类A,当我们调用A中数据时,并不会发生冗余。
此时,D对象内部结构是这样:
在上图中,父类数据并不存放在虚继承的子类中,那么子类怎么找到父类数据呢?
——在虚继承的类中,会定义一个虚基表指针vbptr,指向虚基表。
而虚基表中会存在偏移量,这个量就是表的地址到父类数据地址的距离。
我们可以通过调试,找到虚基表指针和虚基表:
首先,我们为每一个数据赋值,以便观察:
之后,调用监视,查看d对象地址和d中a数据地址:
再通过d的地址查看内存窗口,看d中内存分布:
由此我们可以分析得到对象d及其内部父类的内存布局:
在这个我们可能会有个疑问,那这B和C中两个是什么呢?
这就是虚基表指针!
再通过内存窗口,查看一下虚基表指针指向的地址,根据我们的了解应该就是虚基表,而其中存有偏移量:
而偏移量,就是虚基表指针地址到父类数据地址的距离,这里以b中虚基表为例:
到这里我们就能解释一个问题:为什么bptr和cptr能够找到并不位于自己内部的变量a?
因为bptr和cptr都对d进行了切片,当各自寻找变量a时,会从自身的虚基表指针中找到虚基表,通过虚基表的偏移量找到变量a的地址,从而找到了变量a。
画图解释就是这样:
当使用虚继承的时候,需要注意,虚继承只有在多继承时才有用。也就是说如果只有一层继承关系或者是单继承都将不起作用。
因为虚继承是保证子类中只有一个间接父类,说简单一点就是虚继承只能在隔代继承中起作用。
比如下面两种情况即便虚继承也没有意义:
(1)是因为虽然虚继承产生了虚基表和指针,但是class B并没有子类,而虚继承是用以保证子类只有一个间接父类class A。当然话说回来,就算有子类、哪怕多个子类,也都体现不出虚继承,因为虚继承要求同一个子类的多个父类继承自同一个间接父类,而该例只有一个父类class B。
(2)是因为虽然class C虚继承了class B,但是class B是class A的非虚继承,那么B中就会有一份A。而class D对A是虚继承,就导致E在实例化时会存放一个对D而言公共的A。这样E中还是存放了两个A。调用变量时还是会混淆。
这样说可能还有些难懂,那换个说法,class B中没有虚基表指针,而D中有虚基表指针,当E从D调用int a时会从虚基表指针找到公共区域的A,而E从B中找只会在B的区域内找到int a。
画图表示E内部结构就是这样:
正确的继承关系应该是当class A的子类继承时,都是虚继承,这才能保证当有像class E这样的间接子类定义时,class在其中都只会在公共区域有一份。对本例来说即class B是虚继承。
只有两种编程语言:一种是天天挨骂的,另一种是没人用的——Bjarne Stroustrup(C++之父)
如有错误,敬请斧正
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。