【C++进阶】继承

news2024/11/15 8:43:31

⭐博客主页:️CS semi主页
⭐欢迎关注:点赞收藏+留言
⭐系列专栏:C++进阶
⭐代码仓库:C++进阶
家人们更新不易,你们的点赞和关注对我而言十分重要,友友们麻烦多多点赞+关注,你们的支持是我创作最大的动力,欢迎友友们私信提问,家人们不要忘记点赞收藏+关注哦!!!

继承

  • 前言
  • 一、继承的概念及定义
    • 1、继承的概念
    • 2、继承的定义
      • (1)继承定义格式
      • (2)继承关系和访问限定符
      • (3)继承基类成员访问方式的变化
      • (4)总结
  • 二、基类和派生类对象赋值转换
    • 1、子类赋值给父类(切割)
    • 2、指针 / 引用强制赋值
    • 3、赋值操作就是引用操作
  • 三、继承中的作用域
  • 四、派生类的默认成员函数
    • 1、派生类的构造函数
    • 2、派生类的拷贝构造函数
    • 3、派生类的赋值函数
    • 4、派生类的析构函数
      • 5、具象的图
  • 五、继承与友元
  • 六、继承与静态成员
  • 七、复杂的菱形继承及菱形虚拟继承
    • 1、继承分类
    • 2、菱形继承的问题和解决方法
    • 3、虚拟继承解决数据冗余和二义性的原理
      • (1)不用虚拟继承
      • (2)使用虚拟继承
    • 4、练习题
      • (1)多继承指针偏移问题
      • (2)构造输出顺序问题
  • 八、 继承和组合
    • 1、is - a 和 has - a
    • 2、优先使用对象组合,而不是类继承


前言

继承顾名思义就是将父类的成员往后进行继承,其中有很多的细节供我们发掘。


一、继承的概念及定义

1、继承的概念

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

我们在找工作的时候是需要将个人信息归档的,但是如果我们将每个人的信息都进行放到一个类的话,是不是难免很复杂,如下图:
在这里插入图片描述

现在我们有一个继承的概念让我们很快速上手这个操作:
也就是用一套方式放在那里,延伸出不同的派生类进行规划出不同人,如下格式:

2、继承的定义

(1)继承定义格式

Person是父类,也称作基类。Student是子类,也称作派生类。
在这里插入图片描述

(2)继承关系和访问限定符

在这里插入图片描述

(3)继承基类成员访问方式的变化

巧记:跟着小的来(public>protected>private),成员哪个小跟哪个来。
在这里插入图片描述

我们可以简单先来写一个继承:

#include<iostream>
using namespace std;

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;
	s.Print();
	t.Print();
	return 0;
}

在这里插入图片描述

(4)总结

第一点:不可见是什么意思?
不可见的直面意思就是在这个派生类中的类外面和类里面都不可以被访问。

我们发现基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它。

第二点:protected和private区别
这里的protected和private的区别是在派生类有很大的区别,当我们不想在类外面访问而只想在类里面进行访问的话,就用protecred即可,因为private在派生类中是不能被访问的。

基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的。

第三点:上面的表不需要特别记忆,我们只需要记住一个口诀,基类的其他成员在子类的访问中取小。

基类的私有成员在子类都是不可见。基类的其他成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected > private。

第四点:class – private struct – public

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

第五点:表的前两行和前两列最重要。

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


二、基类和派生类对象赋值转换

1、子类赋值给父类(切割)

顾名思义,也就是父类和子类的对象赋值转换。

派生类对象可以赋值给基类的对象 / 基类的指针 / 基类的引用。这里有个形象的说法叫切片或者切割。寓意把派生类中父类那部分切来赋值过去。基类对象不能赋值给派生类对象。
在这里插入图片描述

2、指针 / 引用强制赋值

基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类的指针是指向派生类对象时才是安全的。

class Person
{
protected:
	string _name = "zhangsan";
	string _sex = "nan";
	int _age = 2;
};
class Student : public Person
{
public:
	int _No;
};

int main()
{
	Student s1;
	// 1.子类对象可以赋值给父类对象/指针/引用
	Person p1 = s1;
	Person* pp = &s1;
	Person& rp = s1;

	//2.基类对象不能赋值给派生类对象
	s1 = p1;

	// 3.基类的指针可以通过强制类型转换赋值给派生类的指针
	pp = &s1;
	Student* ps1 = (Student*)pp;
	ps1->_No = 10;
	return 0;
}

3、赋值操作就是引用操作

我们利用赋值过去的操作就是引用操作,一个成员变量改变了以后,另一个也会跟着一样的改变。

在这里插入图片描述


三、继承中的作用域

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

在这里插入图片描述

  1. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏,也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问)

在这里插入图片描述

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

在这里插入图片描述

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

四、派生类的默认成员函数

1、派生类的构造函数

派生类的构造函数必须调用基类的构造函数初始化基类的那一部分成员。如果基类没有默认的构造函数,则必须在派生类构造函数的初始化列表阶段显示调用。
派生类对象初始化先调用基类构造再调派生类构造。

在这里插入图片描述
在这里插入图片描述

2、派生类的拷贝构造函数

派生类的拷贝构造函数必须调用基类的拷贝构造完成基类的拷贝初始化。

在这里插入图片描述

正确做法:
在这里插入图片描述

3、派生类的赋值函数

派生类的operator=必须要调用基类的operator=完成基类的复制。

在这里插入图片描述

4、派生类的析构函数

派生类的析构函数会在被调用完成后自动调用基类的析构函数清理基类成员。因为这样才能保证派生类对象和派生类成员先清理再清理基类成员的顺序。

为什么析构函数先子类再父类?
因为后续一些场景析构函数需要构成重写,重写的条件之一是函数名相同。那么编译器会对析构函数名进行特殊处理,处理成destrutor(),所以父类析构函数不加virtual的情况下,子类析构函数和父类析构函数构成隐藏关系。
在这里插入图片描述

5、具象的图

在这里插入图片描述


五、继承与友元

友元关系不能继承,也就是说基类友元不能访问子类私有和保护成员

在这里插入图片描述

解决方法:

在这里插入图片描述


六、继承与静态成员

基类定义了static静态成员,则整个继承体系里面只有一个这样的成员。无论派生出多少个子类,都只有一个static成员实例 。
在这里插入图片描述


七、复杂的菱形继承及菱形虚拟继承

1、继承分类

分为三个类:单继承、多继承和菱形继承,其中,菱形继承有两个比较大的问题,我们在下面进行显示:
在这里插入图片描述

2、菱形继承的问题和解决方法

如下代码(菱形继承):

在这里插入图片描述

解决方法:利用虚拟继承虚拟继承可以解决菱形继承的二义性和数据冗余的问题。如上面的继承关系,在Student和Teacher的继承Person时使用虚拟继承,即可解决问题。需要注意的是,虚拟继承不要在其他地方去使用。
在这里插入图片描述
当然下面这种指定类域的方法也是可以的(指定继承只能解决二义性的问题,数据冗余的问题解决不了):
在这里插入图片描述

3、虚拟继承解决数据冗余和二义性的原理

为了研究虚拟继承原理,我们给出了一个简化的菱形继承继承体系,再借助内存窗口观察对象成员的模型。

(1)不用虚拟继承

在这里插入图片描述
数据冗余:
在这里插入图片描述

(2)使用虚拟继承

D对象中将A放到的了对象组成的最下面,这个A同时属于B和C,那么B和C如何去找到公共的A呢?这里是通过了B和C的两个指针,指向的一张表。这两个指针叫虚基表指针,这两个表叫虚基表。虚基表中存的偏移量。通过偏移量可以找到下面的A。
在这里插入图片描述

在这里插入图片描述

为什么要用B、C指针指向虚基表找到偏移量再去通过本身偏移量进行找到所需的值的位置呢?

方便,节省空间,先取到偏移量,再计算内置类型在对象中的地址,再访问。将所有的继承的内置类型的寻找地址用同一个样式,大家都一样,要想找到无论是继承的还是本身的内置类型,都先去找虚基表,从中找到偏移量,再根据偏移量找到内置类型,很方便,无论指针指向还是本身都这么做,大家都一样,数据冗杂性减少,很方便。

在这里插入图片描述

4、练习题

(1)多继承指针偏移问题

多继承中指针偏移问题?下面说法正确的是( )
在这里插入图片描述
在这里插入图片描述

解析及答案:
答案选C
在这里插入图片描述
在这里插入图片描述

(2)构造输出顺序问题

下面程序输出结果是什么? ()

在这里插入图片描述
在这里插入图片描述

解析:先去调用A的构造函数,因为D类是先去调用A的构造函数,调用一次后面的B和C就不需要去重复调用了。调用完父类的构造函数再根据class D : public B, public C 的顺序调用B类再调用C类即可。

答案选A。

八、 继承和组合

在这里插入图片描述

1、is - a 和 has - a

在这里插入图片描述

2、优先使用对象组合,而不是类继承

在这里插入图片描述


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

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

相关文章

收集springboot2.6和2.7新功能新特性,以便后续项目中使用

更多功能看演示系统 gitee源代码地址 后端代码: https://gitee.com/nbacheng/nbcio-boot 前端代码:https://gitee.com/nbacheng/nbcio-vue.git 在线演示(包括H5) : http://122.227.135.243:9888 因为nbcio-boot已经升级到2.7,所以先收集SpringBoot2.6和2.7的新功能,…

数据结构:链表的一些经典的OJ题目

文章目录 写在前面链表OJ调试技巧移除链表元素反转链表链表的中间节点链表中倒数第K个节点链表分割问题 写在前面 本篇为本人学习链表的过程中遇到的典型OJ题&#xff0c;于是整理出来分享思路和便于后续重新学习&#xff0c;每个标题均可跳转至对应习题&#xff0c;大多为Lee…

Autosar通信实战系列02-CAN报文发送周期测试脚本开发及周期不准优化

本文框架 前言1. CAN发送报文的周期测试脚本开发2. 发送报文周期不准的可能原因及优化策略2.1 发送报文的控制逻辑2.2 送报文周期不准的可能原因及优化策略前言 在本系列笔者将结合工作中对通信实战部分的应用经验进一步介绍常用,包括但不限于通信各模块的开发教程,代码逻辑…

nvidia显卡设置 让显卡发挥最大的性能

1、打开官网https://www.nvidia.cn/geforce/drivers/ 查看电脑系统位数和显卡(GPU)的版本 产品系列&#xff1a;Notebooks表示笔记本 2、点击【搜索】-【下载】(game表示游戏驱动)-【下载】 3、双击运行exe文件 4、使用邮箱注册账号时注意要设置正常的年龄 PS设置 1、设置PS使用…

2023 7-29

题目1 删除排序链表重复元素 思路和代码 /*** Definition for singly-linked list.* struct ListNode {* int val;* ListNode *next;* ListNode() : val(0), next(nullptr) {}* ListNode(int x) : val(x), next(nullptr) {}* ListNode(int x, ListNode *n…

某ERP系统信息泄露登录后台

漏洞描述 某ERP前台泄露了某api接口,恶意攻击者可通过调用该接口,对用户的账号和密码进行非授权访问,在获取到账号和密码后,恶意攻击者可接管后台。 免责声明 技术文章仅供参考,任何个人和组织使用网络应当遵守宪法法律,遵守公共秩序,尊重社会公德,不得利用网络从事…

Unity 工具之 NuGetForUnity 包管理器,方便在 Unity 中的进行包管理的简单使用

Unity 工具之 NuGetForUnity 包管理器&#xff0c;方便在 Unity 中的进行包管理的简单使用 目录 Unity 工具之 NuGetForUnity 包管理器&#xff0c;方便在 Unity 中的进行包管理的简单使用 一、简单介绍 二、NuGetForUnity 的下载导入 Unity 三、NuGetForUnity 在 Unity 的…

文件夹加密软件哪个好?文件夹加密软件排行榜

想要加密文件夹&#xff0c;使用专业的文件夹加密软件可以安全有效地加密电脑文件夹。那么&#xff0c;文件夹加密软件哪个好&#xff1f;文件夹加密软件排行榜了解一下。 TOP 1&#xff1a;文件夹加密超级大师 软件亮点&#xff1a;极致安全&#xff0c;5种文件夹加密类型 文…

EMC学习笔记(二十)EMC常用元件简单介绍(二)

EMC常用元件简单介绍&#xff08;二&#xff09; 1.瞬态抑制二极管&#xff08;TVS&#xff09;2.气体放电管3.半导体放电管 电磁兼容性元件是解决电磁干扰发射和电磁敏感度问题的关键,正确选择和使用这些元件是做好电磁兼容性设计的前提。由于每一种电子元件都有它各自的特性,…

谈 Scratch 作品 —— “木筏生存”

资源下载链接&#xff1a; https://download.csdn.net/download/leyang0910/88090187 资源情况&#xff1a; 角色数量&#xff1a;3&#xff0c;素材数量&#xff1a;54&#xff0c;积木数量&#xff1a;6349&#xff0c;音频数量&#xff1a;9 游戏说明&#xff1a; 这是一款…

擎创技术流 | 深入浅出运维可观测工具(二):eBPF应用中常见问题

上期跟大家聊了下eBPF的发展历史还有特性&#xff0c;点击这里↓↓↓擎创技术流 | 深入浅出运维可观测工具&#xff08;一&#xff09;&#xff1a;聊聊eBPF的前世今生&#xff0c;一键回看上期精彩内容。 这期主要跟大家分享下eBPF在应用过程中可能出现的问题&#xff0c;希望…

leetcode 122. 买卖股票的最佳时机 II

2023.7.29 把整体利润拆分成每天的利润&#xff0c;将股票值想象成一个折线图&#xff0c;将所有上升的值相加即可。 代码&#xff1a; class Solution { public:int maxProfit(vector<int>& prices) {int ans 0;for(int i1; i<prices.size(); i){if(prices[i]-…

神经网络简单介绍

人工神经网络(artififial neural network) 简称神经网络&#xff0c;它是一种模仿生物神经网络结构和功能的非线性数学模型。 神经网络通过输入层接受原始特征信息&#xff0c;再通过隐藏层进行特征信息的加工和提取&#xff0c;最后通过输出层输出结果。 根据需要神经网络可以…

树及其遍历

文章目录 树树定义专业术语树分类 二叉树分类存储连续存储&#xff08;完全二叉树&#xff09;链式存储一般树的存储森林的存储 线索二叉树哈夫曼树构造步骤 遍历先序遍历中序遍历后续遍历 链式二叉树遍历具体代码已知两种遍历序列求原始二叉树已知先序和中序求后序已知中序和后…

mtk桌面壁纸滚动

路径&#xff1a;packages\apps\Launcher3\src\com\android\launcher3 目前代码中是未打开的状态 packages\apps\Launcher3\src\com\android\launcher3\util\WallpaperOffsetInterpolator.java 涉及到的方法有&#xff1a;&#xff08;重要性从上往下&#xff09; updateOffs…

架构的分类

目录 一、 RUP41 架构 1.1 RUP41架构方法概述 1.2 RUP41架构总体 1.3 RUP41架构方法内容 1.3.1 逻辑视图 1.3.2 开发视图 1.3.3 物理视图 1.3.4 处理视图 1.3.5 场景视图 ​二、 TOGAF9 架构 2.1 TOGAF9 架构概述 2.2 TOGAF9 架构分类 2.2.1 业务架构 2.2.2 数据架…

Vue2 第四节 计算属性,监视属性

1.计算属性 2.监视属性 3.计算属性与监视属性之间的关系 一.计算属性 定义&#xff1a;要用的属性不存在&#xff0c;要通过已有属性计算得来原理&#xff1a;底层借助了Object.defineproperty方法提供的getter和setterget函数什么时候会执行&#xff1a;初次读取的时候会执…

银河麒麟安装solr及配置文件修改

下载并解压 将从官网下载的solr.tar.gz解压到目标文件夹 解压命令&#xff1a;tar -zxvf solr-xxx.tar.gz 修改配置文件 进入目录&#xff1a; /home/solr/solr-8.5.1/bin/init.d 修改solr文件里面部分配置 修改solr服务路径 SOLR_INSTALL_DIR"/opt/solr" 改为…

app稳定性测试-iOS篇

稳定性测试&#xff1a;测试应用程序在长时间运行过程中是否存在内存泄漏、崩溃等问题&#xff0c;以确保应用程序具有较高的稳定性和可靠性。 对于安卓端&#xff0c;官方提供了很好的稳定性测试工具&#xff1a;monkey。 相比较而言&#xff0c;iOS则没有&#xff0c;而且当前…

#systemverilog# 说说Systemverilog中《automatic》那些事儿(关于for循环内置变量研究)

#systemverilog# 说说Systemverilog中《static》那些事儿(理论篇1) 上面的截图,是来自于前面的文章,当时留下的疑问,今天和大家是实践一下小细节问题。 一 for 内置声明变量修饰 1.1 static 修饰