【C++】继承详解
本篇要分享的内容是关于继承的内容哼哼哼啊啊啊啊啊啊啊啊啊啊啊啊啊啊
以下为本篇目录
目录
1.简单了解继承
2.继承的简单定义
3.继承简单使用
4.继承方式
4.1基类的privat
4.2基类的protected
4.3不可见与private的区别
5.父子类对象赋值转换
6.继承的作用域
7.子类 / 派生类的默认成员函数
7.1默认构造
7.2拷贝构造
7.3析构函数及其原因
首先封装、继承、多态是面向对象的三大特性,本篇先了解继承。
继承(inheritance)机制是一个一个一个面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。
简单来说继承就是可以将代码的内容进行复用
比如我们在学习冒泡排序、堆排序、快速排序这些排序需要对数据进行交换,那么使用交换数据的功能就属于函数集的复用,也就是将公共集的部分抽取出来被调用,这就是之前我们学到的复用。
1.简单了解继承
下面使用代码简单认识继承
可以看到这里定义了两个类,一个学生类一个教师类,我们发现有些数据是两个类中共有的,那么我们就可以单独将这些属性单独领出来,重新创建一个类来使用。
class Person
{string _name;string _id;string _tel;int age;
};
根据上图,具有两个类公共部分的叫做父类,或者基类;在图的下方的两个类就叫做子类,或者派生类;
2.继承的简单定义
我们可以先暂时记住这种继承方法,下文还会详细介绍
3.继承简单使用
用代码简单演示一下继承的使用
class Person
{
public:void Print(){cout << "name:" << _name << endl;cout << "age:" << _age << endl;}protected:string _name = "peter"; // 姓名int _age = 18;
};class Student : public Person
{
protected:int _stuid; // 学号
};class Teacher : public Person
{
protected:int _jobid; // 工号
};
int main()
{Student s;Teacher t;return 0;
}
可以看到如上代码定义了三个类,一个是Person类,还有Student类和Teacher类,并且两个子类中都有自己特有的的元素,学号和工号,在这样的情况下继承了父类并各自创建对象,那这个对象的属性都有什么呢?
通过调试后的监视窗口我们可以看到在使用继承之后,子类创建的对象都具有父类的属性。
同样的也可以继承成员函数
可以看到即使在子类中没有定义函数,在继承了父类之后,子类的对象也可以调用父类中的函数。
4.继承方式
在上面的代码中呢我们会看到一些关键字,比如protected,并且会有疑问那继承方式只能是public吗,接下来我们回到继承的定义
同时也要和之前类和对象中的封装的内容联系起来
我们在之前的封装中谈到过protected(保护)和private(私有),也说过保护和私有没有什么区别, 保护和私有在类的外部都不能访问,共有的在类外和类里面都能访问。
现在我们所接触到的继承中也有三种继承方式。但是这里需要理解的是,在父类中设置访问权限后,继承到子类中是否还设置了同样的权限呢?所以如下表
这个表来表示父类的成员在子类中又怎样的访问关系。
这个表的内容是很重要的,在记忆是要讲究技巧
首先我们可以将父类和子类想象成为父亲和儿子,父亲的私房钱是无论如何都不可以被儿子所知的,所以儿子不可以拿到父亲的私房钱;
我们使用最开始的代码演示
4.1基类的privat
可以看到在父类中将打印函数设为私有,子类对象便不能访问;
同样的不仅是在类的外部,在子类的内部同样不能访问,爸爸的私房钱是不能动的;
4.2基类的protected
在之前的封装中protected和private是没有区别的,但是在继承中两个方式便有了区别
还是上列代码,我们将Print保护起来,在类的外部显然是不能调用的
但是在子类的内部却能被调用;
所以protected在继承中才能体现出它的价值。
当我们通过两个例子之后就会发现继承方式会取在类的基类访问限定符和继承方式中权限小的那一个
所以再次观察这个表格你会发现两者相交权限会取小的限定符进行访问,这就是记忆的技巧,了解他的规律即可。
最后还需要注意的是我们都知道class类在不写继承方式的情况下默认是私有继承
可以看到Student继承父类没有写继承方式,默认为私有继承
但是struct类在不写继承方式的情况下默认为共有继承
可以看到运行成功
但是在这里我希望大家在使用继承时最好最好将继承方式规范写完整。
4.3不可见与private的区别
在上述的表格中我们看到了这两个信息
他们的本质区别就在于:私有属性是类里和类外都不可用,如同父亲的私房钱,怎么样都看不见用不到;
不可见属性是类里可以使用,但是类外不可使用;如同结婚时的彩礼,只能结婚时用,其他时间不能使用;
区别就这么简单。
5.父子类对象赋值转换
我们知道不同类型之间的对象赋值的时候,如果是相近类型会发生隐式类型转换,发生隐式类型转换就会产生中间变量
比如这里d时double类型,double和int类型相似,都是用来记录数据的类型所以可以发生隐式类型转换,但是转化时会产生中间变量,
在这里可以看到使用引用会报错,原因是因为这里的r引用的是隐式转换的中间变量,但是中间变量具有常性,所以需要加const才能完成转换;
那类和对象之间的转化是怎样的呢?
我们使用学生和教师类创建对象,可以看到两个相似的类同样可以发生隐式类型转换。
但是当我们使用引用的时
我们发现这里并没有产生临时对象。
那是不是内置类型隐式转换会产生临时对象,而自定义类型隐式转换不会发生隐式转换呢?
如下例
可以看到还是产生了临时对象
同样需要加上从const;
所以这里有一个结论:public继承时,父类和子类是一个is-a的关系,相当于召唤出替身,
子类的对象赋值给父类的对象、或者父类指针、或者父类引用,我们认为是天然的,中间不产生临时对象,也被称作父子类的赋值兼容规则,也叫做切片。
那is-a是什么关系呢?简单说就是:你有的,我都有
观察下图
当子类继承了父类之后,子类创建的对象可以看作是一个一个一个父类的对象,也就可以直接赋值给父类不会产生中间变量。
那这一小串代码就如下图
6.继承的作用域
父类和子类是有独立的作用域的,所以可以分别再父类和子类中定义相同的变量
虽然子类继承了父类的属性和函数,但是两者的作用域不同。那么有以下问题
class Person
{
protected:string _name = "wdd";int _num = 111;};
class Student : public Person
{
public:void Print(){cout << " 姓名:" << _name << endl;cout << " 身份证号:" << Person::_num << endl;cout << " 学号:" << _num << endl;}
protected:int _num = 999; // 学号
};
在子类定义的打印函数中能否访问到_num?如果能访问,访问的又是谁的_num呢?
测试如下
可以看到_num访问的是Student中的属性;
和我们之前讲到的就近原则同理 。
那我们想要在子类中访问父类的属性加上作用域和访问限定符即可
可以看到还是在子类中,可以通过指定的作用域来访问父类中的属性 。
根据以上来看,我们得出结论:父类和子类可以有同名成员,因为他们是独立作用域;默认情况是直接访问子类的,子类同名成员会隐藏父类同名成员。
同样的成员函数也是如此
class A
{
public:void fun(){cout << "func()" << endl;}
};
class B : public A
{
public:void fun(int i){A::fun();cout << "func(int i)->" << i << endl;}
};
如上代码中两个func函数不是函数重载,而是函数隐藏。
之前我们说过函数重载是在同一个作用域中,函数名相同,函数的参数不同才能构成函数重载,但是如上代码中两个函数的作用域不同
可以看到我们使用B类创建了对象,并且使用其调用fun函数,调用的是B类中的fun函数;
但是我们想要去调用父类的fun函数会出现错误
可以看到它的错误为函数中的参数太少,因为父类的函数是无参的,并且与子类中的函数同名,于是便对父类中的fun函数进行了隐藏;
7.子类 / 派生类的默认成员函数
7.1默认构造
我们之前在构造函数中说过在类中不写构造函数,编译器会默认生成构造函数,在我们写了构造函数便不会生成构造函数,如此观察以下代码和运行结果
可以看到的是1.我们使用子类继承了一个父类2.使用子类创建了一个对象3.在子类中定义任何的构造函数和析构函数4.输出的结果中出现了父类的构造和析构函数
这里的原因是因为这里子类(Student)是有两部分构成的,一部分是子类本身的结构,另一部分是继承了父类的结构,接下来观察父类的构造函数和析构函数
父类内容如下
class Person
{
public:Person(const char* name = "peter"): _name(name){cout << "Person()" << endl;}Person(const Person& p): _name(p._name){cout << "Person(const Person& p)" << endl;}Person& operator=(const Person& p){cout << "Person operator=(const Person& p)" << endl;if (this != &p)_name = p._name;return *this;}~Person(){cout << "~Person()" << endl;}
protected:string _name; // 姓名
};
可以看到的是我们在父类中定义了构造函数和析构函数;
那么接下来我们在子类中写一个构造函数,观察它在创建对象时是否会使用自己的构造函数
可以看到在子类中写了构造函数之后s对象也调用了自家的构造函数。
这里需要注意的是需要初始化父类的内容时需要声明父类中的初始化内容,要将父类的内容当成一个完整的对象去初始化,而不是单个的使用初始化(与之前的匿名对象有些相似)
7.2拷贝构造
复用上面的代码我们观察拷贝构造
使用s1拷贝构造了s2,运行的结果可以看到还是会优先调父类的拷贝构造
我们可以继续在子类中定义拷贝构造的函数
可以看到这里又在子类中定义了拷贝构造函数,在输出的结果中又多了一行
这就是子类调用了自己的拷贝构造函数。
7.3析构函数及其原因
和构造函数相同,自己定义之后编译器就不会默认生成,没有自己定义编译器就会默认成生成,在上面的代码中可以看到运行的结果一直在调用父类的析构函数,析构函数同样可以自己定义
可以看到在最下方我们想要使用复用父类的析构函数却发现析构函数被调用了两次,
这是因为编译器对构造和析构是有顺序的;
我们之前都了解过构造和析构的顺序
构造的顺序是先父后子
析构的顺序是先子后父
这也是为什么上面写的子类的析构函数可以调用父类的成员函数。
简单来说如同没有你的父母就没有你
如果析构函数的顺序是先父后子的话,可能会将父类的数据空间释放调,如果子类需要用到父类的数据时父类已经先被析构,再访问父类数据会有风险。
所以为了保证析构安全,要先子后父
父类析构函数不需要显式调用,子类析构函数结束时会自动调用父类析构函数。
以上就是关于继承的基础内容,如果对你有所帮助还请三连支持,感谢您的阅读。
相关文章:

【C++】继承详解
本篇要分享的内容是关于继承的内容哼哼哼啊啊啊啊啊啊啊啊啊啊啊啊啊啊 以下为本篇目录 目录 1.简单了解继承 2.继承的简单定义 3.继承简单使用 4.继承方式 4.1基类的privat 4.2基类的protected 4.3不可见与private的区别 5.父子类对象赋值转换 6.继承的作用域 7.子…...
react:swr接口缓存
useSWR 是一个 React Hooks,是 HTTP 缓存库 SWR 的核心方法之一。SWR 是一个轻量级的 React Hooks 库,通过自动缓存数据来实现 React 的数据获取。 第一个参数是被缓存的数据的 key, 第二个参数是一个函数,该函数返回数据或者一个…...

2023-11 | 短视频批量下载/爬取某个用户的所有视频 | Python
这里以鞠婧祎的个人主页为demo https://www.douyin.com/user/MS4wLjABAAAACV5Em110SiusElwKlIpUd-MRSi8rBYyg0NfpPrqZmykHY8wLPQ8O4pv3wPL6A-oz 【2023-11-4 23:02:52 星期六】可能后面随着XX的调整, 方法不再适用, 请注意 找到接口 找到https://www.douyin.com/aweme/v1/web/…...

【JAVA学习笔记】66 - 本章作业(IO流)
项目代码 https://github.com/yinhai1114/Java_Learning_Code/tree/main/IDEA_Chapter19/src/com/yinhai/homework 1.使用File类和FileWriter类 (1)在判断e盘下是否有文件夹mytemp,如果没有就创建mytemp public class Homework01 {public static void main(String…...

vscode中 vue3+ts 项目的提示失效,volar插件失效问题解决方案
文章目录 前情提要bug回顾解决方案最后 前情提要 说起来很耻辱,从mac环境换到window环境,vscode的配置都是云端更新过来的,应该是一切正常才对,奇怪的是我的项目环境出现问题了,关于组件的ts和追踪都没有效果ÿ…...

Elasticsearch:在 ES|QL 中使用 DISSECT 和 GROK 进行数据处理
目录 DISSECT 还是 GROK? 或者两者兼而有之? 使用 DISSECT 处理数据 Dissect pattern 术语 例子 DISSECT 关键修饰符 右填充修饰符 (->) 附加修饰符 () 添加顺序修饰符( 和 /n) 命名的跳过键(?…...
基于自适应自回归模型的高级人工智能概念及其实现
基于自适应自回归模型的高级人工智能概念及其实现 摘要:一、引言:二、方法:三、讨论:四、结论:草稿实现计算摘要: 在人工智能研究领域中,预测未来的信息往往会遇到信息不明确的问题,尤其是在自回归模型中,这一问题尤为突出。本研究提出一个新颖的假设,将能自主解决信…...

windows的mysql启动错误,查看windows日志
1、点击左下角开始按钮,计算机上右键,点击【管理】。 2、在计算机管理界面依次找到【系统工具】,选择【时间查看器】,打开【windows日志】,点击【应用程序】 3、在右侧找到,最新的mysql错误信息。双击查看。…...

centos7部署Canal与Canal集成使用
1、简介 canal [kə’nl],译意为水道/管道/沟渠,主要用途是基于 MySQL 数据库增量日志解析,提供增量数据订阅和消费 早期阿里巴巴因为杭州和美国双机房部署,存在跨机房同步的业务需求,实现方式主要是基于业务 trigge…...

C语言--分段函数--switch语句
如何用switch语句写分段函数呢?⭐️ 首先介绍一下switch语句的语法规则⭐️ switch(整形表达式) {case 常量表达式1; //标签必须唯一语句块1;break;case 常量表达式2; //if(a0),而case中时系统自动加语句块2;break;c…...
动态规划31(Leetcode188买卖股票的最佳时机4)
代码: 我的状态方程: buy[i][j]max{buy[i−1][j],sell[i−1][j-1]−price[i]} 题解里的: buy[i][j]max{buy[i−1][j],sell[i−1][j]−price[i]} ..没理解题解的 但我的通过了 class Solution {public int maxProfit(int k, int[] pric…...
npm包管理相关命令
前置条件,准备npm账号,并登录,npm login 或者 npm adduser (这一行同样需要输入账号密码登录,之后就不用登录了) 验证是否登录:npm whoami 还可以查看用户简介:npm profile get …...

2023年Q3乳品行业数据分析(乳品市场未来发展趋势)
随着人们生活水平的不断提高以及对健康生活的追求不断增强,牛奶作为优质蛋白和钙的补充品,市场需求逐年增加。 今年Q3,牛奶乳品市场仍呈增长趋势。根据鲸参谋电商数据分析平台的相关数据显示,2023年7月-9月,牛奶乳品市…...
软考 系统架构设计师系列知识点之边缘计算(2)
接前一篇文章:软考 系统架构设计师系列知识点之边缘计算(1) 所属章节: 第11章. 未来信息综合技术 第4节. 边缘计算概述 3. 边缘计算的特点 边缘计算是在靠近物或数据源头的网络边缘侧,融合网络、计算、存储、应用核心…...

Maven中的继承与聚合
一,继承 前面我们将项目拆分成各个小模块,但是每个小模块中有很多相同的依赖于是我们创建一个父工程将模块中相同的依赖定义在父工程中,然后子工程继承父工程Maven作用:简化依赖配置,统一依赖管理,可以实现多重继承像J…...

第三章 UI开发的点点滴滴
一、常用控件的使用方法 1.TextView android:gravity"center" 可选值:top、bottom、left、right、center等,可以用"|"来同时指定多个值,center表示文字在垂直和水平方向都居中 android:textSize 指定文字的大小&#…...

637. 二叉树的层平均值
描述 : 给定一个非空二叉树的根节点 root , 以数组的形式返回每一层节点的平均值。与实际答案相差 10-5 以内的答案可以被接受。 题目 : 637. 二叉树的层平均值 分析 : 这个题和前面的几个一样,只不过是每层都先将元素保存下来,最后求平均就行了: 解…...

【Java笔试强训】Day9(CM72 另类加法、HJ91 走方格的方案数)
CM72 另类加法 链接:另类加法 题目: 给定两个int A和B。编写一个函数返回AB的值,但不得使用或其他算数运算符。 题目分析: 代码实现: package Day9;public class Day9_1 {public int addAB(int A, int B) {// wr…...

django REST框架- Django-ninja
Django 是我学习的最早的web框架,大概在2014年,当时选他原因也很简单就是网上资料比较丰富,自然是遇到问题更容易找答案,直到 2018年真正开始拿django做项目,才对他有了更全面的了解。他是一个入门有门槛,学…...

数据结构与算法C语言版学习笔记(3)-线性表的链式结构:链表
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言:回顾顺序表的优缺点:为什么要引入链式结构的线性表? 一、什么是链表?二、链表的分类①为什么要设置头节点&…...

Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
可靠性+灵活性:电力载波技术在楼宇自控中的核心价值
可靠性灵活性:电力载波技术在楼宇自控中的核心价值 在智能楼宇的自动化控制中,电力载波技术(PLC)凭借其独特的优势,正成为构建高效、稳定、灵活系统的核心解决方案。它利用现有电力线路传输数据,无需额外布…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...

涂鸦T5AI手搓语音、emoji、otto机器人从入门到实战
“🤖手搓TuyaAI语音指令 😍秒变表情包大师,让萌系Otto机器人🔥玩出智能新花样!开整!” 🤖 Otto机器人 → 直接点明主体 手搓TuyaAI语音 → 强调 自主编程/自定义 语音控制(TuyaAI…...
实现弹窗随键盘上移居中
实现弹窗随键盘上移的核心思路 在Android中,可以通过监听键盘的显示和隐藏事件,动态调整弹窗的位置。关键点在于获取键盘高度,并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

如何在网页里填写 PDF 表格?
有时候,你可能希望用户能在你的网站上填写 PDF 表单。然而,这件事并不简单,因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件,但原生并不支持编辑或填写它们。更糟的是,如果你想收集表单数据ÿ…...
C++课设:简易日历程序(支持传统节假日 + 二十四节气 + 个人纪念日管理)
名人说:路漫漫其修远兮,吾将上下而求索。—— 屈原《离骚》 创作者:Code_流苏(CSDN)(一个喜欢古诗词和编程的Coder😊) 专栏介绍:《编程项目实战》 目录 一、为什么要开发一个日历程序?1. 深入理解时间算法2. 练习面向对象设计3. 学习数据结构应用二、核心算法深度解析…...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...

使用SSE解决获取状态不一致问题
使用SSE解决获取状态不一致问题 1. 问题描述2. SSE介绍2.1 SSE 的工作原理2.2 SSE 的事件格式规范2.3 SSE与其他技术对比2.4 SSE 的优缺点 3. 实战代码 1. 问题描述 目前做的一个功能是上传多个文件,这个上传文件是整体功能的一部分,文件在上传的过程中…...