【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析构函数及其原因

和构造函数相同,自己定义之后编译器就不会默认生成,没有自己定义编译器就会默认成生成,在上面的代码中可以看到运行的结果一直在调用父类的析构函数,析构函数同样可以自己定义

 可以看到在最下方我们想要使用复用父类的析构函数却发现析构函数被调用了两次,

这是因为编译器对构造和析构是有顺序的;

我们之前都了解过构造和析构的顺序

构造的顺序是先父后子

析构的顺序是先子后父

这也是为什么上面写的子类的析构函数可以调用父类的成员函数。

简单来说如同没有你的父母就没有你 

如果析构函数的顺序是先父后子的话,可能会将父类的数据空间释放调,如果子类需要用到父类的数据时父类已经先被析构,再访问父类数据会有风险。

所以为了保证析构安全,要先子后父

父类析构函数不需要显式调用,子类析构函数结束时会自动调用父类析构函数。

以上就是关于继承的基础内容,如果对你有所帮助还请三连支持,感谢您的阅读。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Linux权限:系统中的数字锁与安全之门】

1.Linux下的用户 Linux下有两种用户&#xff1a;超级用户&#xff08;root&#xff09;、普通用户。 超级用户&#xff1a;可以再linux系统下做任何事情&#xff0c;不受限制普通用户&#xff1a;在linux下做有限的事情。超级用户的命令提示符是“#”&#xff0c;普通用户的命令…

计算机网络实验

计算机网络实验 使用软件PT7.0按照上面的拓扑结构建立网络&#xff0c;进行合理配置&#xff0c;使得所有计算机之间能够互相通信。并且修改各交换机的系统名称为&#xff1a;学号_编号&#xff0c;如你的学号为123&#xff0c;交换机Switch0的编号为0&#xff0c;则系统名称为…

基于Jaccard相似度的推荐算法---示例

目录 数据展示推荐算法的分类基于相似度基于流行度/上下文/社交网络 Jaccard相似度分析数据的特点可以考虑的方法计算方法优缺点计算用户之间的Jaccard相似度获取与给定最相似的10个用户对1713353的用户推荐10本书 数据展示 import pandas as pd import numpy as np# 读取CSV文…

如何处理【SVC】中的样本不均衡问题

样本不均衡是指在一组数据集中&#xff0c;标签的一类天生 占有很大的比例&#xff0c;但我们有着捕捉出某种特定的分类的需求的状况。比如&#xff0c;我们现在要对潜在犯罪者和普通人进行 分类&#xff0c;潜在犯罪者占总人口的比例是相当低的&#xff0c;也许只有2%左右&…

CSS 边框、轮廓线

一、CSS边框&#xff1a; CSS边框属性允许指定一个元素边框的样式和颜色。 1&#xff09;、边框样式&#xff1a;border-style属性用来定义边框的样式&#xff0c;border-style值&#xff1a; 2&#xff09;、边框宽度&#xff1a;border-width属性用于指定边框宽度。指定变宽…

Python - Windows下使用Python脚本同步一个文件夹下的所有文件到另一个文件夹下

Python同步文件 在Windows下使用Python脚本同步一个文件夹下的所有文件到另一个文件夹下 示例代码 import logging import os import shutildef sync_files(src_file_path, dst_dir_path, exclude_list):try:if not os.path.exists(src_file_path):os.makedirs(src_file_pat…

RISC Zero ZKP协议中的商多项式

1. 引言 前序博客见&#xff1a; Reed-Solomon Codes及其与RISC Zero zkVM的关系 RISC Zero zkVM主要针对可验证计算&#xff0c;其具有隐私和可扩展属性&#xff1a; Reed-Solomon Codes及其与RISC Zero zkVM的关系博客中指出&#xff1a;RISC Zero中的Reed-Solomon Code…

django+drf+vue 简单系统搭建 (2) - drf 应用

按照本系统设置目的&#xff0c;是为了建立一些工具用来处理简单的文件。 1. 准备djangorestframework 关于drf的说明请参见&#xff1a;Django REST Framework教程 | 大江狗的博客 本系列直接使用drf的序列化等其他功能。 安装 conda install djangorestframework conda i…

Spire.Office for .NET 8.10.2 同步更新-Crk

Spire.Office for .NET是 E-iceblue 提供的企业级 Office .NET API 的组合。它包括Spire.Doc、Spire.XLS、Spire.Spreadsheet、Spire.Presentation、Spire.PDF、Spire.DataExport、Spire.OfficeViewer、Spire.PDFViewer、Spire.DocViewer、Spire.Barcode和Spire.Email。Spire.O…

OceanBase 如何通过日志观测冻结转储流程?

本文旨在通过日志解析 OceanBase 的冻结转储流程&#xff0c;以其冻结检查线程为切入点&#xff0c;以租户&#xff08;1002&#xff09;的线程名为例。 作者&#xff1a;陈慧明&#xff0c;爱可生测试工程师&#xff0c;主要参与 DMP 和 DBLE 自动化测试项目。 爱可生开源社区…

在MacBook上实现免费的PDF文件编辑

之前我想对PDF文件进行简单处理&#xff08;比如删页面、添空白页、调整页面顺序&#xff09;&#xff0c;要么是开wps会员【花钱贵】&#xff0c;下载&#xff08;盗版&#xff09;Adobe Acrobat【macOS不好下载】&#xff0c;要么用福昕阅览器登陆学生账号&#xff08;学校买…

MySQL的存储过程

存储过程&#xff1a;是一组为了完成特定功能的sql语句的集合&#xff0c;类似于函数 写好一个存储过程之后&#xff0c;我们可以像函数一样随时可以调用sql的集合 复杂的&#xff0c;需要很多sql语句联合执行完成的任务 存储过程在执行上比sql语句执行速度快&#xff0c;效率…

错误未找到concrt140.dll最详细的解决方法与修复教程

作为一名长期活跃电脑计算机上的用户&#xff0c;我非常理解找不到concrt140.dll导致无法继续执行代码的困扰。这个问题可能会影响到许多软件的工作进度&#xff0c;甚至影响到项目的完成。在这里&#xff0c;我将分享我对于这个问题的理解和修复方法&#xff0c;希望能对大家有…

劳务派遣公司如何通过网盘与境外用户共享文件数据

中外企业合作、跨国公司已成为趋势&#xff0c;相应的文件数据共享问题应运而生。数据作为现代全球经济的命脉&#xff0c;如果文件数据无法高效流转&#xff0c;就会成为了企业发展的桎梏。 而传统常用的文件协作方式一般是邮件沟通&#xff0c;不过在日常使用过程中&#xf…

jenkins原理篇——成员权限管理

大家好&#xff0c;我是蓝胖子&#xff0c;前面几节我讲述了jenkins的语法以及我是如何使用jenkins对测试和正式环境进行发布的。但正式环境使用jenkins还有一点很重要&#xff0c;那就是权限管理。正式环境的权限往往不能对所有人开放&#xff0c;以及要做到每次发布都是谁在操…

什么是数据可视化,为什么数据可视化很重要?

数据可视化是数据的图形表示&#xff0c;可以帮助人们更轻松地理解和解释复杂的信息。它涉及创建数据的视觉表示&#xff0c;例如图表、图形、地图和其他视觉元素&#xff0c;以传达数据中的见解、模式和趋势。数据可视化是将原始数据转化为可操作知识的关键工具。 以下是数据…

【数据结构】树与二叉树(三):二叉树的定义、特点、性质及相关证明

文章目录 5.1 树的基本概念5.1.1 树的定义5.1.2 森林的定义5.1.3 树的术语5.1.4 树的表示 5.2 二叉树5.2.1 二叉树1. 定义2. 特点3. 性质引理5.1&#xff1a;二叉树中层数为i的结点至多有 2 i 2^i 2i个&#xff0c;其中 i ≥ 0 i \geq 0 i≥0。引理5.2&#xff1a;高度为k的二叉…

【机芯智能】智能公元(语音模块)

语音模块配置 进入语音模块智能公元官网&#xff0c;配置词条和识别后的串口输出指令. 记录下相关指令以及上图的识别词条&#xff0c;方便SDK烧写后的调试 SDK烧写 4. SDK 先和电脑调试助手配合&#xff0c;验证数据

web框架与Django

web应用程序 什么是web Web应用程序是一种可以通过Web访问的应用程序&#xff0c;程序的最大好处是用户很容易访问应用程序&#xff0c;用户只需要有浏览器即可&#xff0c;不需要再安装其他软件 应用程序有两种模式C/S、B/S。C/S是客户端/服务器端程序&#xff0c;也就是说这…

竞赛选题 深度学习疫情社交安全距离检测算法 - python opencv cnn

文章目录 0 前言1 课题背景2 实现效果3 相关技术3.1 YOLOV43.2 基于 DeepSort 算法的行人跟踪 4 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; **基于深度学习疫情社交安全距离检测算法 ** 该项目较为新颖&#xff0c;适合作为竞赛…