C++语法——详解虚继承

目录

一.什么是虚继承

二.虚继承原理

三.虚继承使用注意事项


一.什么是虚继承

所谓虚继承(virtual)就是子类中只有一份间接父类的数据。该技术用于解决多继承中的父类为非虚基类时出现的数据冗余问题,即菱形继承问题。

小编用一张图来表述一下:

如果是下图这种非虚继承,那么D类中就会出现两个 int a,当我们用D实例化对象调用a时,编译会报错,因为发生了混淆。除非指定类域B或C。当然指定A也可以,因为默认会从第一个父类中找。

C++语法——详解虚继承_第1张图片

 此时,D的实例化对象内部结构如下:

C++语法——详解虚继承_第2张图片

而当我们使用虚继承时,结构是下图这样,D中只有一份父类A,当我们调用A中数据时,并不会发生冗余。

C++语法——详解虚继承_第3张图片

 此时,D对象内部结构是这样:

C++语法——详解虚继承_第4张图片

二.虚继承原理

在上图中,父类数据并不存放在虚继承的子类中,那么子类怎么找到父类数据呢?

——在虚继承的类中,会定义一个虚基表指针vbptr指向虚基表

而虚基表中会存在偏移量,这个量就是表的地址到父类数据地址的距离

我们可以通过调试,找到虚基表指针和虚基表:

首先,我们为每一个数据赋值,以便观察:

C++语法——详解虚继承_第5张图片

 之后,调用监视,查看d对象地址和d中a数据地址:

C++语法——详解虚继承_第6张图片

 再通过d的地址查看内存窗口,看d中内存分布:

C++语法——详解虚继承_第7张图片

由此我们可以分析得到对象d及其内部父类的内存布局:

C++语法——详解虚继承_第8张图片

在这个我们可能会有个疑问,那这B和C中两个是什么呢?

C++语法——详解虚继承_第9张图片

 这就是虚基表指针

再通过内存窗口,查看一下虚基表指针指向的地址,根据我们的了解应该就是虚基表,而其中存有偏移量

C++语法——详解虚继承_第10张图片

 而偏移量,就是虚基表指针地址到父类数据地址的距离,这里以b中虚基表为例

C++语法——详解虚继承_第11张图片

到这里我们就能解释一个问题:为什么bptr和cptr能够找到并不位于自己内部的变量a?

C++语法——详解虚继承_第12张图片

因为bptr和cptr都对d进行了切片,当各自寻找变量a时,会从自身的虚基表指针中找到虚基表,通过虚基表的偏移量找到变量a的地址,从而找到了变量a。 

画图解释就是这样:

C++语法——详解虚继承_第13张图片

 

三.虚继承使用注意事项

当使用虚继承的时候,需要注意,虚继承只有在多继承时才有用。也就是说如果只有一层继承关系或者是单继承都将不起作用。

因为虚继承是保证子类中只有一个间接父类,说简单一点就是虚继承只能在隔代继承中起作用。

比如下面两种情况即便虚继承也没有意义:

C++语法——详解虚继承_第14张图片

(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内部结构就是这样:

C++语法——详解虚继承_第15张图片

 正确的继承关系应该是当class A的子类继承时,都是虚继承,这才能保证当有像class E这样的间接子类定义时,class在其中都只会在公共区域有一份。对本例来说即class B是虚继承。

C++语法——详解虚继承_第16张图片

 

 

只有两种编程语言:一种是天天挨骂的,另一种是没人用的——Bjarne Stroustrup(C++之父)


如有错误,敬请斧正

 

你可能感兴趣的:(C++语法,子类和间接父类与虚继承关系,虚基表偏移量是什么及原理,虚基表和虚基表指针及内存中存储,虚继承及内存中父类存储形式,C++语法虚继承原理及使用)