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

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

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

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

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

构造的顺序是先父后子

析构的顺序是先子后父

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

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

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

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

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

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

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

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

相关文章

Linux C语言进阶-D14指针函数

指针函数&#xff1a;指一个函数的返回值为地址量的函数 <数据类型>* <函数名称>&#xff08;<参数说明>&#xff09; { 语句序列; } 返回值&#xff1a;全部变量的地址、静态变量的地址、字符串常量的地址、堆上的地址 注意&#xff1a;不可返回局部变量…

项目管理之如何出道(上)

前言 终于有时间更新了&#xff0c;大家是不是等不及了&#xff1f;那么书接上文&#xff0c;言归正传。 各位盆友&#xff0c;时间之轮已划入夜晚&#xff0c;尝试静下心来&#xff0c;思考一番。 Q1&#xff1a;是否曾经期待自己做一名项目经理&#xff0c;干了几年的coder甚…

【NLP】特征提取: 广泛指南和 3 个操作教程 [Python、CNN、BERT]

什么是机器学习中的特征提取&#xff1f; 特征提取是数据分析和机器学习中的基本概念&#xff0c;是将原始数据转换为更适合分析或建模的格式过程中的关键步骤。特征&#xff0c;也称为变量或属性&#xff0c;是我们用来进行预测、对对象进行分类或从数据中获取见解的数据点的…

软件测试/测试开发丨接口测试学习笔记,TcpDump与WireShark

点此获取更多相关资料 本文为霍格沃兹测试开发学社学员学习笔记分享 原文链接&#xff1a;https://ceshiren.com/t/topic/27859 协议分析工具 网络监听&#xff1a;TcpDump WireShark 代理 Proxy 推荐工具&#xff1a;手工测试charles [全平台]、安全测试burpsuite [全平台 j…

chrome安装vue devtools

不能访问应用商店 如果可以访问应用商店可以往下看 插件源代码 选择shell-chrome&#xff0c;这是官方的插件源码 下载源代码打包 参考教程 点击扩展按钮->管理扩展程序->打开开发者模式->把crx文件拖拽进去即可 可以访问chrome应用商店 插件地址 官方文档地址 选…

VSCode修改主题为Eclipse 绿色护眼模式

前言 从参加开发以来&#xff0c;一直使用eclipse进行开发&#xff0c;基本官方出新版本&#xff0c;我都会更新。后来出来很多其他的IDE工具&#xff0c;我也尝试了&#xff0c;但他们的主题都把我劝退了&#xff0c;黑色主题是谁想出来&#xff1f;&#x1f602; 字体小的时…

二维码智慧门牌管理系统升级解决方案:轻松实现辖区范围门址统计

文章目录 前言一、系统功能与优势 前言 在这个数字化时代&#xff0c;传统的门牌管理系统已经无法满足现代管理的需求。为了满足辖区内门址的统计需求&#xff0c;我们引入了全新的二维码智慧门牌管理系统升级解决方案。这一升级将让您轻松实现辖区范围门址的统计&#xff0c;…

开源的全能维护 U 盘工具:Ventoy

开源的全能维护 U 盘工具&#xff1a;Ventoy 本篇文章聊聊迄今为止&#xff0c;我用着最舒服的一款开源 U 盘启动工具&#xff0c;Ventoy。 写在前面 好久不见&#xff0c;接下来计划写一个比较连续的内容&#xff0c;就先从最小的处着手吧。 经过长久的折腾&#xff0c;除…

pytest 的使用===谨记

发现用例的规则 a) 文件test_.py开头和_test.py结尾 b) Test开头的类中test开头的方法&#xff08;测试类不能带有__init__方法&#xff09; c) 模块中test开头的函数&#xff08;可以不在class中&#xff09; 注意点&#xff1a; pytest是以方法为单位发现用例的&#xff0c;你…

吴恩达《机器学习》6-4->6-7:代价函数、简化代价函数与梯度下降、高级优化、多元分类:一对多

一、代价函数 逻辑回归的代价函数是用来度量模型预测与实际结果之间的差异的。与线性回归使用均方误差不同&#xff0c;逻辑回归使用的代价函数在数学上更为复杂。为了理解逻辑回归的代价函数&#xff0c;首先要明白逻辑回归的假设函数&#xff1a; ℎ&#x1d703;(&#x1…

激光雷达和人工智能

几十年来&#xff0c;激光雷达一直是许多行业中非常有用的工具&#xff0c;但直到最近&#xff0c;随着人工智能&#xff08;AI&#xff09;解决方案的引入&#xff0c;我们才开始认识到它的真正潜力。激光雷达&#xff0c;又称光探测和测距&#xff0c;是一种遥感技术。它利用…

React构建组件的方式有哪些?区别?

一、是什么 组件就是把图形、非图形的各种逻辑均抽象为一个统一的概念&#xff08;组件&#xff09;来实现开发的模式 在React中&#xff0c;一个类、一个函数都可以视为一个组件 在之前文章中&#xff0c;我们了解到组件所存在的优势&#xff1a; 降低整个系统的耦合度&am…

nginx下载安装和日志切割

目录 一、nginx安装配置 1.nginx版本 2.nginx安装配置 3.查看安装后的nginx 4.配置PATH变量 二、日志切割 1.给当前日志文件重命名 2.等待 3.写bash脚本 4.查看日志结果 5.加入crontab定时任务 结语 一、nginx安装配置 1.nginx版本 nginx如今分为商业版&#xff0…

imagettftext(): Could not find/open font 解决办法

问题&#xff1a;Captcha验证码不能正常显示&#xff0c;是因为使用GD库imagettftext()函数时&#xff0c;报“Warning: imagettftext(): Could not find/open font in ”警告 。 网上的解决方法: 将font路径的相对路径 转成 绝对路径即可 $fontfile "./fonts/*.ttf&q…

MaHDE

FHM means ‘fitness hierarchical mutation’&#xff0c;DGS means ‘directed global search’&#xff0c;ELS means ‘elite local search’ 辅助信息 作者未提供代码

使用数据分析,识别设备异常

设备健康监测系统在工业领域中扮演着至关重要的角色&#xff0c;它能够帮助企业及时发现设备异常&#xff0c;预防故障&#xff0c;提高设备使用寿命和生产效率。而异常诊断技术则是设备健康监测系统中的核心部分&#xff0c;能够实现对设备异常情况的准确判断。根据设备状态数…

高校为什么需要企业数据库?

随着信息化数字化的发展&#xff0c;企业数据库已经成为高校不可或缺的一部分。企业数据库一般整合了多维度企业数据信息。比如&#xff0c;艾思依托丰富的数据沉淀和领先的模型算法&#xff0c;打造“1N”产业大数据平台&#xff0c;包含“1个企业数据中心”一一涵盖全国2.4亿…

【uniapp】解决在H5谷歌浏览器下 u-input 标签 设置只读后,click事件不生效

【问题描述】 谷歌浏览器更新后&#xff0c;h5模式下原本的input外层view中的click事件不触发了?? 但是更换浏览器后就可以&#xff0c;打包app也是正常可以触发的&#xff0c;本来是没打算兼容h5&#xff0c;既然遇到了就记录一下~ 【解决办法】 使u–input里写上readonly&…

【python海洋专题四十】海洋指数画法--单色填充图

【python海洋专题四十】海洋指数画法–单色填充图 【python海洋专题四十】海洋指数画法–单色填充图 数据:AMO_index 图像展示: 图片 往期推荐 图片 【python海洋专题一】查看数据nc文件的属性并输出属性到txt文件 【python海洋专题二】读取水深nc文件并水深地形图 【p…

[PHP]得推跑腿O2O系统 v3.41

得推跑腿系统是一个以phpMySQL进行开发的主要针对本地跑腿服务的O2O系统&#xff0c;支持wap\\小程序\\App。 主要功能模块&#xff1a; 用户端&#xff1a; 1.跑腿任务发布 2.跑腿任务管理追踪 3.在线支付 4.常用地址管理 跑腿端&#xff1a; 1.跑腿任务抢单 2.跑腿员认证 3.…