【C++】浅谈虚继承

C++——浅谈虚继承

问题1:什么是虚继承?为什么要虚继承?

   虚继承 :是面向对象编程中的一种技术,是指一个指定的基类,在继承体系结构中,将其成员数据实例共享给从这个基类型直接或间接派生的其它类。虚拟继承是多重继承中特有的概念。虚拟基类是为解决多重继承而出现的。

概念可能不是那么好理解 ,那么我们看如下案例:
【C++】浅谈虚继承_第1张图片

  上述案例中,羊与骆驼是分别是动物的2个派生类,2者之间都继承了动物的数据和函数,而羊驼又分别继承了羊与骆驼的数据,也就是继承了2分数据如下代码:

#include
#include
using namespace std;

class Animal
{
public:
	int M_age ;
    void func(){cout<<"Animal::func"<

  上述案例是一个菱形继承,那么什么是菱形继承呢?

菱形继承:两个派生类继承同一个基类而又有某个类同时继承两个派生类。该继承被称为菱形继承,或者钻石继承

上述继承存在的问题:

​  1.羊和驼都继承了动物的数据和函数,当Sheeptuo调用函数或者数据时会产生二义性

  2.Sheeptuo继承了两份数据,我们实际只需要一份数据,浪费存储空间

  问题1中二义性可以使用作用域,指定调用哪个基类方式解决,那么重复继承怎么解决,因此我们引入虚基类,如下代码:

#include
#include
using namespace std;

class Animal
{
public:
	int M_age ;
    void func(){cout<<"Animal::func"<

  我们在Sheep与Tuo类的继承方式即public前加virtual关键字,则Sheep与Tuo采用虚继承方式继承Animal,Animal被称为虚基类,此时st可以直接调用M_age和func(),没问题,运行上述代码结果如下:
在这里插入图片描述

  从结果中可以看出有3个20,可见我们的3个类对象共同操作一份数据,符合实际需求那么其内部又是如何实现的呢?

问题2:虚继承实现原理

  这里先说一个工具,叫做开发人员命令提示符如下图,在你安装的vs文件夹下,或者直接开始->所有程序->找到你所装版本的vs文件,点击->发人员命令提示符,就ok了。
在这里插入图片描述
1.打开 开发人员命令提示符  2.定位到当前你工程所在文件的磁盘,(我是在桌面放的 ) 输入方式 C:   3.输入 cd+空格+当前工程所在目录 + 回车 ,(图A)  4.输入dir+回车(图B)  5.输入 cl /d1 reportSingleClassLayout+类名+空格 +(xxx.cpp) 例如cl /d1 reportSingleClassLayoutSheeptuo test.cpp 回车 (见图C)
在这里插入图片描述
​                 A . cd+空格+当前工程所在目录
【C++】浅谈虚继承_第2张图片
​                   B. dir+回车
【C++】浅谈虚继承_第3张图片
​           C. cl /d1 reportSingleClassLayout+类名+空格 +(xxx.cpp)
【C++】浅谈虚继承_第4张图片
​                  D  没有虚继承之前

  从C、D布局图可以看出编译器为我们的对象加了一个vbptr(virtual base pointer),vbptr指向一张表,这张表(虚基类表vbtable)保存了当前的虚指针相对于虚基类的首地址偏移量。

  Sheeptuo派生于Sheep与Tuo,继承了两个基类的vbptr指针,并调整了vbptr与虚基类的首地址的偏移量

内部实现

1.找到Sheep的偏移操作量 :在这里插入图片描述
  首先取到st的地址 &st,然后其内部步长为(int*) &st,现在还没找到Sheep的虚基类表,在取星*(int*) &st

  找到Sheep的虚基类表的第一个位置,再告诉表内步长 (int * )* (int * )&st,然后(int * )* (int* )&st+1找到第一个位置,最后在整体强转为(int* )再取* 最终形式为* (int* )((int* )* (int* )&st + 1),执行此代码,结果显示8

cout<<*(int*)((int*)*(int*)&st + 1) << endl;

Tuo的偏移操作量为在这里插入图片描述
  执行结果为4, 更印证了sizeof(Sheeptuo)大小为12。

2.输出M_age:

通过Sheep找到M_age,st取地址将其强转为(cahr*)(步长为1),那么(char* )&st为0,再加上Sheep的偏移量(char* )&st+(* (int* )((int* )* (int* )&st + 1)),此时其类行为(Animal* ),将其强转为(Animal* )((char* )&st+(* (int* )((int* )* (int* )&st + 1)))并指向M_age:将其输出,结果为20
在这里插入图片描述

3:总结

  1.虚继承可以解决二义性问题,解决重复继承问题,节省内部存储空间

  2.虚继承只能解决具备公共祖先的多继承所带来的二义性问题,不能解决没有公共祖先的多继承,可参见如下(https://www.jianshu.com/p/ab96f88e5285)

你可能感兴趣的:(c++,C++,虚继承)