继承杂谈。

在这里插入图片描述

内容一览

      • 前言
      • 继承的概念及定义
      • 继承的意义
      • 继承关系及访问限定符
      • 父类和子类对象之间的转化
      • 继承后的作用域
      • 继承与有元
      • 继承与静态成员
      • 多继承
      • 继承和组合的区别:
      • 继承的总结和反思

前言

 面向对象的三大特性:封装继承和多态,这三种特性优者很紧密地联系,但是也有很大的区别。
 对于刚接触面度对象编程风格的小白,你可能不了解继承到底是用来做什么的,以及为什么叫做继承,接下来我就带你仔细了解继承的作用以及注意事项。

继承的概念及定义

 继承是代码可以复用的重要手段,术语表达继承就是子类继承父类的属性和方法,大白话就是子承父业,子类可以拥有父类除了私有以外的成员和方法,还可以有自己的属性,父类通常也叫做基类,子类有时也叫做派生类。
 继承呈现了面向对象程序设计的层次结构,体现了由简单到复杂的认知过程,在之前我们在程序中的复用通常是函数复用,传入不同的参数,函数就可以产生不同的响应,但是在学完继承和多态后,代码的复用就不仅仅局限于函数的复用,继承就是类设计层次的复用。
举一个例子理解一下

class animal
{
public:void Paint(){cout << age << endl;}
protected:int age=10;
};class cat :public animal
{
protected:int price = 500;
};

 动物是一个较大的概念,在动物的种类中有猫,小狗等等,他们具有都是动物的特点,也与属于他们自己的特点,继承animal后,cat就是animal的子类,animal就是cat的父类。
在这里插入图片描述
如上图所示,如果用子类构造出一个对象,子类也包含了父类的成员和方法。
此时,用父类函数构造出来的子类就也可以使用父类的方法。
在这里插入图片描述

继承的意义

 现在我们要思考了,为什么我们需要继承呢?向上变得例子来看,继承并没有带来太大的优化,这样你就大错特错了,如果仅仅只有两三个类,在每个类的属性和方法都确定的请款下确实某有必要实现继承,但是在现实中通常是一整个系统程序,往往有很多类且大部分类都是具有相似部分的,如果我们每个类都丹毒重新写,不仅代码臃肿而且工作量大,出bug不易查寻。
 有了继承之后,我们可以将部分类中相似的部分提取出来作为父类,在继承父类后添加自己新的属性和方法,这样不仅可以大大减少代码量,还易于维护,代码结构清晰可见。

继承关系及访问限定符

 C++中有三种访问限定符,分别是public,private,protected,继承方式也就有了三种,最常用的就是public继承。
在这里插入图片描述
如上图所示
私密性public<protected<private。
 刚开始时是没有保护这个限定符的,正式出现继承和多态之后才加上的,父类的私有子类不能访问,但父类的保护子类也可以访问。

父类和子类对象之间的转化

子类的对象可以赋值给父类的对象/父类的指针/父类的引用,这里有一种很形象的说法就是切片或者切割,子类的对象继承了父类,所以包含父类的一部分,在赋值给父类对象时,将子类父类的一部分切割给父类对象。
切记!只有子类对象可以赋值给父类。

	cat c;animal a = c;//子类对象赋值给父类animal* a = &c;//赋值子类对象的指针animal& k = c;//赋值子类对象的引用

赋值切片的过程类似于
在这里插入图片描述

继承后的作用域

 继承后子类和父类仍然是两个独立的作用域。所以要注意这个问题,如果子类和父类包含有同名函数,这两个函数之间的关系不是重载(重载要求两个函数在同一作用域),而是隐藏,子类会隐藏父类的同名函数。
 要注意哈!在父子类中,只要两函数的名称相同就构成隐藏。但是在实际中最好不要在父子类中定义同名函数,不然会容易混淆。
 如果构成隐藏后,我们调用该函数就调用的是子类的虚函数,如果我们想要调用父类的虚函数,就要加上作用域(在子类成员函数中,可以使用基类::基类成员,显式访问)。

class person
{
public:void func1(){cout << " person::func1" << endl;}
};
class student : public person
{
public:void func1(){cout << "func1" << endl;}
};int main()
{person p;student s;p.func1();s.func1();return 0;
}

在这里插入图片描述
 运行后发现,父类调用该函数使用的父类中的,子类调用就调用子类中的函数,子类将父类的同名函数进行隐藏,这样搞调用时就不会产生歧义。
接下来探索探索继承后,子类的成员函数和弗雷德成员函数之间的关系。

  • 1,首先,子类的构造函数必须先调用父类的构造函数初始化子类中所包含父类的一部分,如果父类中没有默认的构造函数,就必须在子类构造函数的初始化列表进行初始化。
  • 2. 派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。
  • 3. 派生类的operator=必须要调用基类的operator=完成基类的复制。
  • 4. 派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象先清理派生类成员再清理基类成员的顺序。
  • 5. 派生类对象初始化先调用基类构造再调派生类构造。
  • 6. 派生类对象析构清理先调用派生类析构再调基类的析构。

通过调试来带领大家观察构造及西狗屎的顺序。
在这里插入图片描述
 可以发现,在构造子类对象时,再初始化列表首先调用父类的构造函数,在析构时,首先调用子类的析构函数,然后再调用父类的析构函数。
在这里插入图片描述

继承与有元

 我们知道一个类的有元可以在类外使用类中的变量和方法,那么父类的友元函数能被继承吗?
 答案是不能,友元关系不能被继承,基类的有缘不能访问子类的私有和保护。
举一个例子

class A
{
public:
private:int _a=2;
};
class B:public A
{
public:int _b = 0;friend void Printf();
};
void Printf(B b,A a)
{cout << b._b;cout << a.a;
}
int main()
{A a;B b;Printf(b,a);return 0;
}

 在子类中创建一个友元函数,友元函数只可以访问,而不是父类的友元函数,所以父类中的保护和私有我们这个友元函数不能访问。
在这里插入图片描述

继承与静态成员

 基类定义了static静态成员,整个继承体系里面只有一个这样的成员,无论有多少个子类,静态成员就只有这一个。

class A
{
public:int _a = 2;static int aa;
};
int A::aa=1;
class B :public A
{
public:int _b = 0;
};
int main()
{A a;B b;A::aa++;cout<<a.aa<<endl;a.aa++;cout << b.aa;return 0;
}

 由于两个类中的静态变量只有一份,所以我们可以直接用类加作用域直接访问,也可以用对象进行访问。
如图
在这里插入图片描述

多继承

单继承:一个子类只有一个直接父类时称这个继承关系为单继承。
例如:
在这里插入图片描述

多继承:一个子类同时继承多个父类,这种关系称为多继承
在这里插入图片描述
有了多继承就会产生一种特殊的情况,那就是菱形继承
在这里插入图片描述
这种情况就会产生特殊的影响
 假如A中有一个元素,那么BC分别继承了A,然后D再次继承BC,D中就包含了两份这个元素。如果我们去调用D中A的元素(假设为_a)就会出现二义性。
在这里插入图片描述
 不仅如此,我们D类中包含了两份_a,还会浪费内存。我们可以使用作用域去看。
在这里插入图片描述
如何解决数据重复问题呢?
我们可以使用关键字virtual。
使用虚拟继承就可以解决问题
虚拟继承可以解决菱形继承的二义性和数据冗余的问题。
关键字virtual在多态时也会用到,在这里是修饰继承了最上边的父类的子类。在上边的例子中就是修饰B和C。
在这里插入图片描述
正如上图,也是菱形继承的一种,我们要在距离被继承两次的父类的下一层子类加virtual,使其变成虚拟继承。
但是还是那句话,我们在实际写程序中尽量不要写菱形继承。
如下图
在这里插入图片描述
此时再来观察在内存中是如何解决这件问题的。
借用上边的例子
在这里插入图片描述
我们来调试运行看一看。
在这里插入图片描述
 可以发现,不管我们调用B还是C对象中的_a元素,都是调用的同一个,来看一看底层如何实现的,以及为什么将A类中的_a放在最后的位置。
我们在X86的环境下来观察
在这里插入图片描述
 可以发现,在B和C对象前都有一个指针,这个指针里存的就是该位置到_a的距离,当我们想要修改A中_a的值时,就会找到该位置距离A对象中_a的长度,然后修改A中_a的值。
 将A中数据放在末尾就是为了统一管理,方便每一个继承该父类的子类通过指针找到该位置。

继承和组合的区别:

public是一种is-a的关系,也就是说每个派生类对象都是一个基类对象。
组合是一种has-a的关系,假设B组合了A,每个B对象中都会有一个A对象。
在这里插入图片描述
优先使用对象组合,而不是类继承。
 继承允许你根据基类的实现来定义派生类的实现。这种通过生成派生类的复用通常被称
为白箱复用(white-box reuse)。术语“白箱”是相对可视性而言:在继承方式中,基类的
内部细节对子类可见 。继承一定程度破坏了基类的封装,基类的改变,对派生类有很
大的影响。派生类和基类间的依赖关系很强,耦合度高。
 对象组合是类继承之外的另一种复用选择。新的更复杂的功能可以通过组装或组合对象
来获得。对象组合要求被组合的对象具有良好定义的接口。这种复用风格被称为黑箱复
用(black-box reuse),因为对象的内部细节是不可见的。对象只以“黑箱”的形式出现。
组合类之间没有很强的依赖关系,耦合度低。优先使用对象组合有助于你保持每个类被
封装。
 实际尽量多去用组合。组合的耦合度低,代码维护性好。不过继承也有用武之地的,有
些关系就适合继承那就用继承,另外要实现多态,也必须要继承。类之间的关系可以用
继承,可以用组合,就用组合。

继承的总结和反思

 继承大大增加了我们代码的复用性,有人说C++语法复杂,其实就是多继承的问题,比建议设计出多继承,java就吸取了C++的教训,没有引入多继承,如果使用多继承不小心设计出了菱形继承,那会在复杂度和时间上都产生问题。
本文结束,如果有问题或者有疑问就直接评论哦,回复很快。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/735323.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Prometheus】PromQL

数据类型 即时向量&#xff08;instant vector&#xff09; node_cpu_seconds_total{instance"ahoj-dev-ubuntu-virtualbox",mode"idle"} 区间向量&#xff08;range vector&#xff09; node_cpu_seconds_total{instance"ahoj-dev-ubuntu-virtu…

手拉手RocketMQ基础

消息中间件的对比 消息中间件 ActiveMQ RabbitMQ RocketMQ kafka 开发语言 java erlang java scala 单击吞吐量 万级 万级 10万级 10万级 时效性 ms us ms ms 可用性 高(主从架构) 高(主从架构) 非常高(主从架构) 非常高(主从架构) 消息中间件: activ…

云上攻防-云产品篇堡垒机场景JumpServer绿盟SASTeleport麒麟齐治

知识点 1、云产品-堡垒机-产品介绍&攻击事件 2、云产品-堡垒机-安全漏洞&影响产品 章节点&#xff1a; 云场景攻防&#xff1a;公有云&#xff0c;私有云&#xff0c;混合云&#xff0c;虚拟化集群&#xff0c;云桌面等 云厂商攻防&#xff1a;阿里云&#xff0c;腾讯…

CSS拖曳盒子案例

让我为大家带来一个小案例吧&#xff01; <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style>* {margin: 0;padding: 0;}.box1 {width: 100px;height: 100px;background-color: black;margin-bot…

iMazing3 2024详细解析数据备份与恢复备份

iMazing 3的备份功能支持增量备份&#xff08;类似苹果电脑里的Time Machine功能&#xff09;&#xff0c;意思是第一次把移动设备的数据全部备份下来&#xff0c;之后的备份就只针对数据有变化的那部分&#xff0c;这样可以节省大量的时间和存储空间&#xff0c;不会让使用者为…

LeetCode59:螺旋矩阵Ⅱ

题目描述 给你一个正整数 n &#xff0c;生成一个包含 1 到 n2 所有元素&#xff0c;且元素按顺时针顺序螺旋排列的 n x n 正方形矩阵 matrix 。 示例 1&#xff1a; 输入&#xff1a;n 3 输出&#xff1a;[[1,2,3],[8,9,4],[7,6,5]] 代码 class Solution { public:vector…

00-ESP-IDF 环境配置指南

ESP-IDF 环境配置指南 ESP-IDF安装 1.首先我们在浏览器搜索esp-idf&#xff0c;点击第一个选项 2.点击右边栏的安装 3.我们选择手动安装选择需要的系统版本 4.点击链接 5.这里我们选择一个版本&#xff0c;建议不要选择最新的&#xff0c;安装出现问题在网上不好找到解决办…

蓝桥杯备战刷题-滑动窗口

今天给大家带来的是滑动窗口的类型题&#xff0c;都是十分经典的。 1&#xff0c;无重复字符的最长子串 看例三&#xff0c;我们顺便来说一下子串和子序列的含义 子串是从字符串里面抽出来的一部分&#xff0c;不可以有间隔&#xff0c;顺序也不能打乱。 子序列也是从字符串里…

Vue+SpringBoot打造个人健康管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 健康档案模块2.2 体检档案模块2.3 健康咨询模块 三、系统展示四、核心代码4.1 查询健康档案4.2 新增健康档案4.3 查询体检档案4.4 新增体检档案4.5 新增健康咨询 五、免责说明 一、摘要 1.1 项目介绍 基于JAVAVueSpri…

【周总结周末日常】

周总结 完成任务开发并且与前端联调通过 完成已开发功能的冒烟测试 修复测试中出现的一些数据显示问题 2024/3/10 晴 温度适宜 这周天气比上周好多了&#xff0c;最起码见到好几次太阳 周六在世纪公园溜达一会儿&#xff0c;偶尔呼吸下大自然&#xff0c;挺棒的…

【QT】创建第一个QT程序

下面的前7个可以先不看&#xff0c;直接从8开始看 1. 创建Qt程序 一个Qt程序的组成部分&#xff1a;应用程序类&#xff0c;窗口类应用程序类个数&#xff1a;有且只有一个QApplication a;如何查看类对应的模块&#xff1a;光标移动到类上&#xff0c;F1qmake模块的名字 2. …

【设计模式】(四)设计模式之工厂模式

1. 工厂模式介绍 工厂模式&#xff08;Factory Pattern&#xff09;是 Java 中最常用的设计模式之一。这种类型的设计模式属于创建型模式&#xff0c;它提供了一种创建对象的最佳方式。 工厂模式有三种实现方式&#xff1a; 简单工厂模式工厂方法模式抽象工厂模式 2. 工厂方…

前后端分离项目,如何解决跨域问题?

跨域问题是前后端分离项目中非常常见的一个问题&#xff0c;举例来说&#xff0c;编程猫学习网站的前端服务跑在 8080 端口下&#xff0c;后端服务跑在 9002 端口下&#xff0c;那么前端在请求后端接口的时候就会出现跨域问题。 403 Forbidden 是HTTP协议中的一个状态码&#x…

华容道问题求解_详细设计(五)之hash值和回放功能

&#xff08;续上文&#xff09; 布局的hash 值计算 笔者也参考了之前的一些文章&#xff0c;很多文章提到了怎么节省存贮空间来查找最优解&#xff0c;这不是笔者的目的。笔者的目的比较单一&#xff0c;就是找到最优解就行了。因此并没有在存贮上面进行过多的优化&#xff…

消息队列 MQ

文章目录 1. MQ 相关概念1.1 什么是 MQ1.2 为什么要用 MQ1.3 MQ 分类1.4 MQ 的选择 1. MQ 相关概念 1.1 什么是 MQ MQ(message queue)&#xff0c;从字面意思上看&#xff0c;本质是个队列&#xff0c;FIFO 先入先出&#xff0c;只不过队列中存放的内容是 message 而已&#x…

阿里云DSW做AI绘画时的显卡选择A10?V100?

V100是Volta架构&#xff0c;A10是Ampere架构&#xff0c;架构上讲A10先进点&#xff0c;其实只是制程区别&#xff0c;用起来没区别。 V100是HBM的内存读取&#xff0c;带宽大&#xff0c;但是DDR5的。 二块卡都是全精度为主的算力卡&#xff0c;半精度优势不明显。 需要用…

FPN(Feature Pyramid Network)

参考&#xff1a; 【目标检测】FPN(Feature Pyramid Network) - 知乎 Feature pyramid network是CVPR2017年的一篇文章&#xff0c;它在目标检测中融入了特征金字塔&#xff0c;提高了目标检测的准确率&#xff0c;尤其体现在小物体的检测上。 1. 动机(Motivation) 识别不同…

【读书笔记】针对ICS的ATTCK矩阵详解(一)

Techniques - ICS | MITRE ATT&CKhttps://attack.mitre.org/techniques/ics/ 一、初始访问&#xff08;Initial Access&#xff09; 该阶段&#xff1a;攻击者正在尝试进入ICS环境。 初始访问包括攻击者可能用作入口向量&#xff0c;从而可以在 ICS 环境中获得初始立足点的…

pytorch实现分割模型TransUNet

TransUNet是一个非常经典的图像分割模型。该模型出现在Transformer引入图像领域的早期&#xff0c;所以结构比较简单&#xff0c;但是实际上效果却比很多后续花哨的模型更好。所以有必要捋一遍pytorch实现TransUNet的整体流程。 首先&#xff0c;按照惯例&#xff0c;先看一下…

逼疯快递员的送货上门,谁来背锅?

快递上门的问题近几年来一直争论不休。 最近&#xff0c;随着新修订的《快递市场管理办法》正式实施&#xff0c;这个话题又成为了焦点。 消费者希望快递能够送上门省去麻烦&#xff0c;快递员希望统一送到代收点提高效率。 是消费者要求太高&#xff1f;快递员太过怠慢&…