C++ 继承【一篇让你学会继承】

news2024/9/20 6:40:10

1. 继承的概念及定义

1.1 继承的概念

继承机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特征的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面向对象程序设计的层次结构,填了由简单到复杂的认知过程。继承是类设计层次的复用。

1.2 继承定义

1.2.1 定义格式

1.2.2 继承关系和访问限定符

1.2.3 继承基类成员访问方式的变化

总结:

1.基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类内还是类外都不能去访问它。除非使用父类中公有的成员函数,通过这个公有的成员函数,间接的访问父类中的私有成员。

(抽象:你继承了你父亲的产业,但是你父亲的私房钱,你是不可以继承使用的。你父亲的私房钱是实际存在的,如果真的想用父亲的私房钱,就需要通过间接的方式,比如让你父亲请客,这样就间接的使用到父亲的私房钱。)

通过上图的调试,可以看出基类private成员还是被派生类对象继承了,我们通过了公有的Print成员函数,访问到了父类中的“_name”和“_age”。

如果我们不通过公有成员函数访问父类中的私有,就会报错(如下图)

如果基类成员不想在类外直接被访问,但需要在派生类中访问,那就将private改成protected。(如下图)

 2.通过上面的表格,我们可以发现,基类的其他成员在子类的访问方式==Min(成员在基类的访问限定符,继承方式),public>protected>private。

比如:

但是protected的继承方式,子类中是可以调用Public成员函数的。(如下图)

再比如,子类通过私有方式继承父类,子类继承父类中所有的成员(包含公有成员、保护成员、私有成员(私有成员不能直接访问)),但这些成员在子类中变成了私有。

3.使用关键字class时默认的继承方式是private,使用strcut时默认的继承方式是public,不过最好显示的写出继承方式。

5.在实际运用中一班使用都是public继承,几乎很少使用protected/private继承,也不提倡使用protected/private继承,因为protected/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。

2. 基类和派生类对象赋值转换

派生类对象可以赋值给基类的对象/基类的指针/基类的引用。这里有个形象的说法叫切片或者切割。指把派生类中父亲的那部分切来赋值过去。

我们先来看一个其他东西。(如下图)

上图,b在给a当别名的时候,因为类型不匹配,产生的临时变量是常量,导致权限放大,所以加上了const。这是类型转换。

而我们的赋值转换,在引用的时候,并没有加上const(如下图)。因为他们两个不是一个东西,赋值转换是一个特殊语法规则。注意区分!

赋值兼容转换主要适用于公有继承的情形。在私有或保护继承的情况下,这种转换通常会导致编译错误或者运行时错误。因为私有继承和保护继承改变了基类成员的访问性,基类的接口与派生类的实际内部结构不匹配。

③基类对象不能赋值给派生类对象

④基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引。但是必须是基类的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI的dynamic cast来进行识别后进行安全转换。(以后的文章会涉及,这边初步了解)

通过下图,我们可以直观看见子类对象可以赋值给父类对象/指针/引用,并且父类对象指向/引用子类中父类的部分。

3. 继承中的作用域

1.在继承体系中基类和派生类都有独立的作用域。

2. 子类和父类中有同名成员,子类成员将屏蔽父亲对同名成员的直接访问,这种情况叫隐藏,也叫重定义。

这个不是重载,重载是在同一作用域下,而父类和子类是两个独立的作用域,出现同名,是隐藏。

(在子类成员函数中,可以使用基类::基类成员 显示访问)

正如下图中,父类有Public,则调用父类的

如果子类有,则调用Public

所以,查找规则就是就近原则有子类用子类,没有子类用父类。

如果有子类的情况下,非要调用父类的话,可以使用基类::基类成员 显示访问(如下图)

3.需要注意的是如果成员函数的隐藏,只需要函数名相同就构成隐藏。

即使参数不一样

4.注意在实际中在继承体系里面最好不要定义同名成员。

4.派生类的默认成员函数

默认构造函数:①我们没有写编译器自己生成的 ②全缺省的 ③不需要传参的

1.派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。(可以把父类当做一个整体,相当于是子类中的一个自定义成员对象)

如果子类调用默认构造,但是父类中没有默认构造,就会报错

修改方式如下图,在子类中显示调用父类的构造函数

要不然就是父类中也有一个默认构造函数

2.拷贝构造函数

如果不需要深拷贝,可以不用我们自己写,使用编译器生成的默认拷贝构造完成拷贝初始化

如果在子类中需要深拷贝,则在子类中手动完成拷贝构造,并及时传父类部分,完成父类的拷贝

 如果父类中需要深拷贝,直接使用子类传来对应父类的值,再完成深拷贝

3.operator=

如果不需要赋值深拷贝,直接使用编译器自动生成的默认赋值

如果需要子类中需要赋值深拷贝,则在子类中手动完成operator=,并及时传父类部分,完成父类部分的赋值(如下图)

如果父类中需要赋值深拷贝,直接使用子类传来对应父类的值,再完成赋值深拷贝

 4.完成构造的时候都是先父后子初始化列表初始化顺序是按照声明的顺序初始化的,而父类是最先声明的,所以在子类初始化列表中,无论是否显示切片传值给父类,都是先初始化父类。所以构造是可以保证先父后子的。

5.完成析构函数是先子后父。如果顺序相反,则可能导致资源管理上的问题,特别是当基类和派生类都包含需要清理的资源时。

为了保证先子后父,在完成子类的析构函数后,自动调用父类的的析构函数清理基类成员。

如果遇到这种情况呢(如下图),下图直接就只调用了父类的析构,导致子类中资源没有被清理。

表面上析构是~+类名,其实本质上析构函数都是同一个名字destructor,所以这边父类和子类的析构就构成了隐藏的关系。这边ptr是父类对象,在析构时就调用了父类的析构函数。

那怎么样我们才能调用到子类的析构函数呢?(如下图)

正如上图所示,在父类的析构函数上面加一个virtual。ptr是父类对象,但是指向的是子类,加上virtual,形成多态,就可以指向谁就调用谁的函数。这样这边就调用了子类的析构,再自动调用父类的析构函数。(关于多态,将会在本篇文章的下一篇文章中呈现)

5. 继承与友元

友元关系不能被继承

也就是说基类友元不能访问子类私有和保护成员。

除非基类友元也是子类的友元

可以这样抽象理解,父亲的朋友不是你的朋友(基类友元不能访问子类私有),除非你和你父亲的朋友成为朋友(基类友元也成为子类的友元)。

6. 继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。

无论派生出多少个子类,都只有一个static成员实例。

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

单继承:一个子类只有一个直接父类时称这个继承关系为单继承

多继承:一个子类有两个或者以上直接父类时称这个继承关系为多继承

菱形继承:菱形继承是多继承的一种特殊情况

 菱形继承的问题:菱形继承有数据冗余(数据重复)和二义性(两种意思)的问题。

上图可以看出在Assistant中,他的两个父类Student和Teacher都包含Person,所以Assistant中有两个Person。导致访问_name的时候,不知道访问的是Student中的_name,还是Teacher中的_name。

在下图中,通过指定作用域的方式,解决了数据二义性的问题。

怎么办呢?既可以解决二义性问题,又可以解决数据冗余问题。

虚拟继承可以解决菱形继承的二义性和数据冗余的问题。

正如下图中,在Student和Teacher继承时加上了virtual了之后,这样在Assistant的对象a中Person的内容就只保留了一份。

8. 继承的总结和反思

1. 很多人说C++语法复杂,其实多继承就是一个体现。有了多继承,就存在菱形继承,有了菱形继承就有菱形虚拟继承,底层实现就很复杂。所以一般不建议设计出多继承,一定不要设计出菱形继承。否则在复杂度及性能上都有问题。

2. 多继承可以认为是C++缺陷之一,很多后来的语言都没有多继承,如Java。

3. 继承和组合

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

 好了,本篇文章就到这里~

欢迎指正批评~

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

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

相关文章

基于JavaWeb开发的java springboot+mybatis电影售票网站管理系统前台+后台设计和实现

基于JavaWeb开发的java springbootmybatis电影售票网站管理系统前台后台设计和实现 🍅 作者主页 网顺技术团队 🍅 欢迎点赞 👍 收藏 ⭐留言 📝 🍅 文末获取源码联系方式 📝 🍅 查看下方微信号获…

【Linux实践】实验二:LINUX操作基础

【Linux实践】实验二:LINUX操作基础 实验目的实验内容实验步骤及结果1. 打开终端2. 关闭计算机命令3. 查看帮助文档4. 修改计算机主机名5. 显示月历和时间6. 统计行数、字符数、单词数 这章开始要涉及到命令了,其他关于命令的内容可以看我 2021年写的笔记…

量水堰计的校准与维护:确保测量结果的准确性

量水堰计作为水利工程中用于测量和调节水流量的重要设备,其准确性和可靠性直接关系到水利设施的正常运行及数据收集的精度。因此,定期校准与维护量水堰计是确保测量结果准确性的关键步骤。本文将从量水堰计的校准方法和周期,以及日常维护保养…

wifi贴码推广能赚钱吗?wifi贴码怎么跟商家沟通?

大家好,我是鲸天科技千千,大家都知道我是做开发的,平时会给大家分享一些互联网相关的创业项目和网络技巧,感兴趣的可以给我点个关注。 最近WiFi这个项目很多朋友来问我,我是前两年就接触过这个,所以比较了…

望繁信科技携流程智能解决方案亮相CNDS 2024新能源产业数智峰会

9月13日,CNDS 2024中国新能源产业数智峰会在北京圆满落幕。本次峰会以“走向数字新能源”为主题,汇聚了来自新能源领域的顶尖领袖、专家学者及知名企业代表,共同探讨数字化技术在新能源行业中的创新应用和发展趋势。上海望繁信科技有限公司&a…

中秋出游热度十足!喆啡酒店如何巧妙捕捉多元旅游需求?

中秋假期临近,多家旅游OTA平台陆续发布旅游热度预测,皆认为中秋小长假有望延续暑期旅游热度。马蜂窝大数据显示,“中秋去哪”关键词近一周热度环比上涨110%,且“中秋3日游”关键词的热度更是大涨175%。消费趋势方面,受…

CAT1 DTU软硬件设计开源资料分析(TCP协议+GNSS定位版本 )

一、CAT1 DTU方案简介: 远程终端单元DTU,一种针对通信距离较长和工业现场环境恶劣而设计的具有模块化结构的、特殊的计算机测控单元,它将末端检测仪表和执行机构与远程控制中心相连接。 奇迹TCP DTUGNSS版本DTU,用于将远程现场的…

【面试干货】软件测试面试题汇总

我把软件测试面试的整个题库都搬来啦,面试能拿下80%,剩下就看你满不满意公司的开价咯。以下答案都是我自己写的,大家根据自己的经历稍作改动,答案仅供参考哦!题库持续更新,需要PDF版可以点击文末小卡片领取…

Unity3d 以鼠标位置点为中心缩放视角(正交模式下)

思路整理: 缩放前: 缩放后: 记录缩放前鼠标的屏幕坐标 A,计算鼠标位置对应的世界坐标 A_world 缩放完成后,根据当前屏幕下A所对应的世界坐标A1_world 计算A1_world 和 A_world 的偏移量 移动摄像机 代码&#xff…

将5s1的搜索难度曲线二次归一化

在行列可自由变换的平面上,5点结构只有34个 (A,B)---6*30*2---(0,1)(1,0) 分类A和B,让A是34个5点结构,让B全是0。收敛误差为7e-4,收敛199次取迭代次数平均值, 让训练集A-B矩阵的高分别是5,6.当高为5的时候…

陪护小程序|陪护小程序成品|陪护小程序源码

陪护系统是为提供病人及其家属更好的服务而开发的一种软件系统。在开发陪护系统时,有一些注意事项是需要考虑的。 首先,需要明确陪护系统的主要功能和目标群体。陪护系统可以包括病人信息管理、医护人员协作、医药管理、预约挂号等功能。我们需要确定开发…

项目管理 | 一文读懂什么是敏捷开发管理

在快速变化的商业环境中,项目管理方式也在不断演进,其中敏捷开发管理因其高效、灵活和适应性强的特点,逐渐成为众多企业和团队的首选。本文将详细解析敏捷开发管理的定义、具体内容及其核心角色,帮助读者全面理解这一先进的项目管…

Python基础语法(3)上

函数 函数是什么 编程中的函数和数学中的函数有一定的相似之处. 数学上的函数,比如 y sin x,x 取不同的值,y 就会得到不同的结果 编程中的函数是一段可以被重复使用的代码片段 代码示例:求数列的和,不使用函数 …

教育培训小程序开发,简单实用的入门指南

教育培训小程序可以帮助教育机构和个人老师提供更灵活的在线教学服务,满足学生的学习需求。对于初学者来说,开发一个功能齐全的教育培训小程序并不复杂,只需掌握一些基础的开发知识和工具即可。本文将带你了解如何使用微信小程序开发工具&…

如何准备技术面试?

大家好,我是老三,好久没更新了,翻出之前的一篇旧稿,是一篇总纲性质的文章——如何准备一场技术面试。这篇文章原本的开头是写给金三银四的,转眼就“金九银十”了,每一年都是最差的一年,又是未来…

跨平台开发新视角:利用Android WebView实现Web内容的原生体验

在移动应用开发领域,跨平台解决方案一直是一个热门话题。开发者们不断寻求能够同时在iOS和Android平台上提供一致用户体验的方法。而Android的WebView组件,作为一个强大的工具,允许开发者在Android应用中嵌入Web内容,为用户提供接…

Python(三)-流程控制

目录 Python三大基本语句 if语句 if-else语句 if..elif...else... if嵌套语句 for循环 while循环 死循环 while嵌套 break语句 continue语句 案例-猜拳游戏 Python三大基本语句 (1)顺序语句 (2)分支语句 &#xff…

P3489 [POI2009] WIE-Hexer

*原题链接* 最短路状态压缩 不愧是POI的题,看题面知道要求加了一些限制的最短路,看数据范围很容易想到状态压缩。 求解最短路就用堆优化dijkstra好了。 至于状态压缩,我们对原数组再开一维,表示此时“剑的集合”,相…

设计模式之组合设计模式

一、组合设计模式概念 组合模式 (Component) 是一种结构型设计模式,将对象组合成树形结构以表示“部分-整体”的层次结构。 组合模式使得用户对单个对象和组合对象的使用具有唯一性。 适用场景 想要表示对象的部分-整体层次结构。想要客户端忽略组合对象与单个对象的…

基于扣子(Coze)打造第一个智能体——个性化对话机器人

文章目录 一,智能体体验二,动手打造一个自己的智能体1,主页点击创建机器人1.1 创建一个新的机器人1.2 修订Bot基础信息1.3 工具编排信息修订人设和回复逻辑、增补开场白等 2,使用插件优化机器人3,使用工作流优化机器人…