C++菱形继承和虚继承的实现

 更新时间:2023年06月01日 11:23:25   作者:小龙向钱进  
本文主要介绍了C++菱形继承和虚继承的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

菱形继承和虚继承本身就是一个"bug",甚至在C++程序员当中有"谁用谁尚阿比"的说法。至于为什么要谈菱形继承和虚继承,那就是因为面试官要问。

1.什么是菱形继承和虚继承

C++作为"第一个吃螃蟹的人",勇敢地设计出了多继承的语法,多继承出现之后,由于一些顶尖程序员的脑洞非常大,就发现了菱形继承所带来数据冗余和二义性的问题,C++标准委员会为了解决这个问题,就设计出了虚继承。从此之后,后面"抄作业的人"就没有多继承的语法,例如java。

2.菱形继承所带来的问题

先理解一段简单的代码:

/*B、C继承自A---D继承自B、C
 *从而构成菱形继承*/
class A
{
public:
        int _a;
};
class B : public A
{
public:
        int _b;
};
class C : public A
{
public:
        int _c;
};
class D : public B, public C
{
public:
        int _d;
};
int main()
{
        D d;
        //d._a = 3; // 报错,_a不明确
        d.B::_a = 3;
        d.C::_a = 8;
        return 0;
}

这段代码的调试结果为:

这就很好解释了二义性的问题,因为在D类对象当中存在了两份A类对象,所以要访问D类对象中的A类对象时必须指明访问,否则就会触发二义性。如果在某些应用场景中,两份A类对象确实是多余的,那么就又触发了数据冗余问题。所以菱形继承存在数据冗余和二义性的问题。下面给出这段程序的继承关系示意图和D类对象模型示意图:

3.虚继承的解决方案

在介绍如何解决菱形继承的问题之前,先理解一段简单的虚拟单继承的代码:

class A
{
public:
        int _a = 1;
};
class B : virtual public A // virtual为虚继承关键字
{
public:
        int _b = 2;
};
int main()
{
        B b;
        return 0;
}

调试-内存窗口截图如下:

如上图所示,B类对象中的A类对象不再存储成员变量,而是存储一个未知值,这个位置本应该存储A类对象的成员变量,但是A类的成员变量却跑到了B类对象的最后。如此类推,如果再有一个C类虚继承自A类,那么C类对象模型也应该像上图一样。

解决菱形继承的方案就是在继承体系的"腰部"使用虚继承,以下面这段代码为例:

class A
{
public:
    int _a = 1;
};
class B : virtual public A
{
public:
    int _b = 2;
};
class C : virtual public A
{
public:
    int _c = 3;
};
class D : public B, public C
{
public:
    int _d = 4;
};
int main()
{
    D d;
    /*都不报错了,他们操作的都是同一个_a*/
    d._a = 1;
    d.B::_a = 3;
    d.C::_a = 8;
    return 0;
}

最终调试的结果如下:

不要被监视窗口所误导,上图三个红色箭头所指向的_a实际上是同一个_a,也就是说D类对象的模型当中只存在一份A类对象了。

通过内存窗口观察D类对象的模型:

与之前介绍的一样,B类对象和C类对象当中本该存储A类对象的位置存储了一个随机值。实际上这个随机值是一个指针,它指向了虚基表。

3.1虚基表

对于上面的图片,介绍了所谓的"随机值"是指针,指向了一个名为虚基表的东西,那么再另起一个内存窗口,观察虚基表的构成:

由此可见,虚基表存储的有效内容为偏移量,具体的来说,当某一指针或引用指向D类对象时,需要访问_a时,就需要通过虚基表当中的偏移量来确定访问目标的位置。虽然虚基表的存在增加了几次指针的运算,但是试想以下,如果A类对象足够大,在菱形继承体系中不使用虚继承,那么最终的D类对象就会有两份A类对象,并且A类对象是一个巨大的对象,那么如果使用了虚继承,就能将两份A类对象压缩成一份A类对象。

所以使用虚继承,能够解决菱形继承带来的数据冗余和二义性问题。最后以一张图描述D类对象的模型:

4.继承与组合

组合的类设计方式是这样的:

class A
{
public:
        int _a;
};
class B
{
public:
        A a;
};

可以明显看出与继承的差别:组合的耦合度更低,继承的耦合度更高。实际上在真实的设计环境当中是很忌讳高耦合的,但是某些场景当中却不得不这么做。

继承是一种is-a的关系,例如下面这个例子:

class Person
{};
class Student : public Person
{};

这个例子所表达的意思就是Student是Person,即学生是人。

组合是一种has-a的关系,例如最开头的那段代码,表达的意思就是B类对象当中有一个A类对象。

针对不同的场景使用不同的复用手段,当条件只允许使用is-a的关系时就使用继承;只允许使用has-a的关系时就使用组合;当既可以使用继承又可以使用组合的关系时使用组合。

为什么要尽量使用组合关系?

因为对于继承来说,它相当于一种白箱复用,即箱子里面的内容能够清清楚楚的看到;对于组合来说,它相当于一种黑箱复用,即箱子里面的内容大多是不可见的,能够看见的也仅仅是一部分(例如设计类时提供给外部的成员函数)。对于继承来说,如果基类的非private成员发生了变动,由于耦合度高的原因,派生类也将会受到影响;对于组合来说,被包含的对象只有public成员发生变动时,才有可能影响到包含该对象的对象。

到此这篇关于C++菱形继承和虚继承的实现的文章就介绍到这了,更多相关C++菱形继承和虚继承内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C语言解决螺旋矩阵算法问题的代码示例

    C语言解决螺旋矩阵算法问题的代码示例

    这篇文章主要介绍了C语言解决螺旋矩阵算法问题的代码示例,螺旋矩阵中的数字由第一行开始到右边不断变大,向下变大,向左变大,向上变大,如此循环...需要的朋友可以参考下
    2016-04-04
  • C++之编写高效Makefile文件最佳方法

    C++之编写高效Makefile文件最佳方法

    在软件开发过程中,Makefile是一个非常重要的工具,它可以帮助我们自动化构建、编译、测试和部署,然而,编写高效的Makefile文件并不是一件容易的事情。在本文中,我们将讨论如何编写高效的Makefile文件,以提高开发效率和产品质量,需要的朋友可以参考下
    2023-05-05
  • C++语言数据结构 串的基本操作实例代码

    C++语言数据结构 串的基本操作实例代码

    这篇文章主要介绍了C语言数据结构 串的基本操作实例代码的相关资料,需要的朋友可以参考下
    2017-04-04
  • VC编程控件类HTControl之CHTGDIManager GDI资源管理类用法解析

    VC编程控件类HTControl之CHTGDIManager GDI资源管理类用法解析

    这篇文章主要介绍了VC编程控件类HTControl之CHTGDIManager GDI资源管理类用法解析,需要的朋友可以参考下
    2014-08-08
  • C语言实现3个数从小到大排序/输出的方法示例

    C语言实现3个数从小到大排序/输出的方法示例

    这篇文章主要给大家介绍了关于如何利用C语言实现3个数从小到大排序/输出的相关资料,文中通过示例代码介绍的非常详细,对大家学习或者使用C语言具有一定的参考学习价值,需要的朋友们下面来一起学习学习吧
    2019-05-05
  • C语言动态内存分配图文讲解

    C语言动态内存分配图文讲解

    给数组分配多大的空间?你是否和初学C时的我一样,有过这样的疑问。这一期就来聊一聊动态内存的分配,读完这篇文章,你可能对内存的分配有一个更好的理解
    2023-01-01
  • C++编程模板匹配超详细的识别手写数字实现示例

    C++编程模板匹配超详细的识别手写数字实现示例

    大家好!本篇文章是关于手写数字识别的,接下来我将在这里记录我的手写数字识别的从零到有,我在这里把我自己的写代码过程发出来,希望能帮到和我一样努力求知的人
    2021-10-10
  • C语言静态动态两版本通讯录实战源码

    C语言静态动态两版本通讯录实战源码

    这篇文章主要为大家带来了C语言实现静态动态两版本的通讯录实战源码,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步
    2022-02-02
  • C++设计模式之简单工厂模式的实现示例

    C++设计模式之简单工厂模式的实现示例

    这篇文章主要给大家介绍了关于C++设计模式之简单工厂模式的相关资料,简单工厂模式,主要用于创建对象,添加类时,不会影响以前的系统代码,需要的朋友可以参考下
    2021-06-06
  • c语言通过栈判断括号匹配是否配对

    c语言通过栈判断括号匹配是否配对

    前面实现了栈的基本数据结构,这里来做一个联系,用栈来解决一道比较常见的算法题,就是括号配对是否满足规则,文中有相关的代码示例供大家参考,需要的朋友可以参考下
    2023-09-09

最新评论