当前位置: 首页 > news >正文

C++继承(下)

文章目录

  • 1. 继承与友元
  • 2. 继承与静态成员
  • 3. 复杂的菱形继承及菱形虚拟继承
    • 3.1 虚拟继承解决数据冗余和二义性的原理
  • 4. 继承和组合

1. 继承与友元

举个例子:
在这里插入图片描述
从上面可以看出:
友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员

如果我们想让这个函数能调用子类的成员,我们也需要让这个函数也成为子类的友元:
在这里插入图片描述

2. 继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员无论派生出多少个子类,都只有一个static成员实例
举个例子:
在这里插入图片描述
这里我们让Graduate继承Student,让Student继承Person。
在这里插入图片描述
然后我们再用这些类去定义一些对象,这样每定义一个对象里面都有父类继承下来的count成员变量,也就会调用到Person类的构造函数。
在这里插入图片描述
从这里我们也可以看到结果证明:无论派生出多少个子类,都只有一个static成员实例。

3. 复杂的菱形继承及菱形虚拟继承

单继承:一个子类只有一个直接父类时称这个继承关系为单继承
在这里插入图片描述
多继承:一个子类有两个或以上直接父类时称这个继承关系为多继承
在这里插入图片描述
这个多继承是什么意思呢?比如说:在大学中,你是一名研究生,你的身份既是一名学生,也是一名老师。这样就有了两种关系,也就继承了两个类。

菱形继承:菱形继承是多继承的一种特殊情况
在这里插入图片描述
而菱形继承的形成是因为学生身份和老师身份又去继承了人这个身份。但是这样会出现一些问题:
在这里插入图片描述
菱形继承的问题:从上面的代码中,可以看出菱形继承有数据冗余和二义性的问题。在Assistant的对象中Person成员会有两份
在这里插入图片描述
这样,我们定义一个对象去调用_name,我们不知道调用的是那一个。该怎么解决呢?我们来看:
在这里插入图片描述
虽然解决了二义性的问题,但是没有解决数据冗余的问题。而数据冗余会给我们造成空间浪费。
虚拟继承可以解决菱形继承的二义性和数据冗余的问题
在这里插入图片描述
如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其它地方去使用

3.1 虚拟继承解决数据冗余和二义性的原理

为了研究虚拟继承原理,我们给出了一个简化的菱形继承继承体系,再借助内存窗口观察对象成员的模型。
在这里插入图片描述
我们以这份代码为例。但是这里我们要注意一个问题:我们要看内存窗口,监视窗口被vs优化后的,看起来可能不是太准确。
在这里插入图片描述
可以看到里面有两个_a。然后我们看看虚拟继承的内存图
在这里插入图片描述
我们看到此时_a放到最下面了。
在这里插入图片描述
B里面的_a放在了这里。
在这里插入图片描述
C里的_a也放在这里。
在这里插入图片描述
它的解决方法是把这个_a放到了一个公共区域。那么此时这里B,C里面的两个指针是干嘛的呢?我们来看一下:
在这里插入图片描述
我们看到这个指针指的是空,下面是一个十六进制的14,转换成10进制为20。
在这里插入图片描述
它指向的也是空,下面十六进制的0c,转换成十进制为12。

那么这两个数字是干什么的呢?答案是:它们是距离_a存储位置的偏移量
可以看到B的指针(0x00F4F988)加上20,就会指向_a(0x00F4F99C)。
可以看到C的指针(0x00F4F990)加上12,就会指向_a(0x00F4F99C)。

这里的作用就是找到菱形虚拟继承的成员变量的位置。这两个指针也被叫做虚基表指针,指针指向的表叫做虚基表。

可能有的同学会问:为什么D中B和C部分要去找属于自己的_a
那么大家看看当下面的赋值:

D d;
B b = d; 
C c = d;

此处的赋值,B和C都需要找到d里的_a才能赋值过去。

那么用static来修饰成员变量能不能解决数据冗余的问题呢
答案是:不可以。
在这里插入图片描述
两个D对象,它们的_a是不一样的。
在这里插入图片描述
但是static定义的_a两个是一样的。所以这里不能用static解决问题。

4. 继承和组合

public继承是一种is-a的关系。也就是说每个派生类对象都是一个基类对象。
组合是一种has-a的关系。假设B组合了A,每个B对象中都有一个A对象。

举个例子:
在这里插入图片描述
那么什么时候使用继承,什么时候使用组合呢
像人<-学生,动物<-狗这些我们比较适合使用继承。车->轮胎,电脑->键盘我们这些可以使用组合。而两者都可以使用的时候,优先使用对象组合。
为什么优先使用对象组合呢

继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称为白箱复用(white-box reuse)
术语“白箱”是相对可视性而言:在继承方式中,基类的内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很大的影响。派生类和基类间的依赖关系很强,耦合度高
像父类的公有成员子类可以直接用,父类的保护成员子类也可以直接用。

对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复用(black-box reuse)
因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被封装。
像C类的公有成员D可以直接用,C类的保护成员D不可以直接用。

相关文章:

  • PKI证书签发系统(2.0web版)
  • Ubuntu20.4下安装TensorFlow2.x
  • 【项目部署】Python Django部署到阿里云
  • MySQL数据库索引并没有你想的那么难之第一节
  • 数据结构与算法-单链表
  • 记一次git误操作, 合并冲突别人新增文件显示成“自己新增“绿色文件
  • Dubbo----------------------------配置信息整合SpringBoot的三种方式
  • 基于视觉的车道线识别技术在智能车导航中的应用研究
  • bleu-mp 多进程bleu评估工具
  • webpack多进程打包
  • 索尼IMX316 标定_ToF模块相机校准
  • 【Proteus仿真】【51单片机】智能鱼缸系统设计
  • 瑞吉外卖2.0 Redis 项目优化 Spring Cache MySQL主从复制 sharding-JDBC Nginx
  • 2023-02-04 Elasticsearch 倒排索引的理解 Trie前缀树原理
  • 【DIY小记】VMWare设置主机连接到的Ubuntu虚拟机的网络端口
  • Spring Boot 集成Quartz
  • 【Java学习】JUC并发编程
  • 【入门AUTOSAR网络管理测试】CANoe测试T_STARTx_AppFrame时间
  • Apache Shiro身份验证绕过(CVE-2023-22602)
  • Cadence PCB仿真 使用 Allegro PCB SI 为电源网络分配电压并选择仿真的电源网络的方法图文教程
  • 电加热油锅炉工作原理_电加热导油
  • 大型电蒸汽锅炉_工业电阻炉
  • 燃气蒸汽锅炉的分类_大连生物质蒸汽锅炉
  • 天津市维修锅炉_锅炉汽化处理方法
  • 蒸汽汽锅炉厂家_延安锅炉厂家
  • 山西热水锅炉厂家_酒店热水 锅炉
  • 蒸汽锅炉生产厂家_燃油蒸汽发生器
  • 燃煤锅炉烧热水_张家口 淘汰取缔燃煤锅炉
  • 生物质锅炉_炉
  • 锅炉天然气_天燃气热风炉