【C++】------继承(一)

news2024/9/21 5:39:23

目录

前言

一、概念与定义

Ⅰ、是什么?

Ⅱ、定义 

1.定义格式:

2.继承方式和访问限定符

3.基类(父类)成员访问方式的变化

二、父类与子类的赋值转化

基本认识

原理 

三、 继承中的作用域

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

构造函数

析构函数 

拷贝构造/赋值重载 

五、继承与友元

 六、继承与静态成员


前言

前面我们对STL库中的一些简单的容器进行了学习,也算是初级内容,从这里开始,我们将更加深入去感受C++的魅力!相信我,你会更喜欢C++!!!

一、概念与定义

Ⅰ、是什么?

先从字面上看,继承继承,无非就相当于现实生活中继承家业,把父亲的事业传给儿子,同时,儿子也能够在此基础上创造出属于自己的东西,也能算作一种改进或者拓展!

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

举个例子:

//一个人统一的信息
class Person
{
public:
	void Print()
	{
		cout << "Maybe i am father!" << endl;
		cout << "name:" << _name << endl;
	}
protected:
	string _name="Stephen Curry";// 姓名
};

//子类继承了父类Person
class Student : public Person
{
protected:
	int _stuid; // 学号
};

int main()
{
	Student s;
	s.Print();
	return 0;
}

上面就是一种继承关系,Student类继承了父类Person,继承后,子类中都会存在一份父类的数据(成员函数+成员变量),这就是体现Student子类复用父类的成员!

所以对于上面这段代码,当子类去调用print函数并且他自己没有和父类相同名字的成员函数时,他实际上就是在调用父类的print函数(原因在下文的作用域)

Ⅱ、定义 

1.定义格式:

2.继承方式和访问限定符

继承方式public继承
private继承
protected继承
访问限定符public
private
protected

3.基类(父类)成员访问方式的变化

public继承protected继承private继承
基类中的public成员派生类的public成员派生类的protected成员派生类的private成员
基类中的protected成员派生类的protected成员派生类的protected成员派生类的private成员
基类中的private成员派生类不可见派生类不可见派生类不可见

总结:实际上访问限定不能存在权限的放大,只能是缩小,也就是满足一个公式:

基类的其他成员在子类的访问方式 == Min(成员在父类的访问限定符,继承方式)public > protected > private。

正如上表:该成员在父类是public,子类是以private的方式继承父类,那么取最小权限,该父类成员在子类的访问方式就变成了private的!!

注意:

派生类不可见,不是说子类没有继承,而是子类也会继承父类的私有成员,但是子类不能直接访问父类的私有成员,可以间接使用(调用父类的方法即可),这就好像是你不能直接用你爸爸的私房钱一样。

②如果父类成员不想在类外直接被访问,但是需要给子类直接访问,就定义该成员为protected。。可见protected因继承而生!

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

二、父类与子类的赋值转化

基本认识

  • 子类对象可以赋值给父类对象,父类指针,父类引用。
  • 父类对象不能赋值给子类对象

如下:父类和子类代码

class Person
{
protected:
	string _name = "Stephen Curry";// 姓名
	string _sex="man";
};

//子类继承了父类Person
class Student : public Person
{
private:
	int _stuid=30; // 学号
};

上述代码可以存在以下赋值关系:

Student s;

Person p = s;//直接赋值给父类对象
Person& refp = s;//赋值给父类引用
Person* p = &s;//赋值给父类指针

 不能存在如下关系:

Student st;
Person pe;
st = pe;//父类不能赋值给子类对象

原理 

 实际上对于上面的赋值关系,有一个形象的说法叫做切片或者切割,也就是说子类会将继承父类的那一部分切割下来赋值给父类。

子类赋值给父类

子类赋值给父类引用,子类切割出的父类那部分的别名!

子类赋值给父类的指针,指针指向的是子类切割出的父类那一部分的首地址!

不难看出,每个子类对象实际上都是一个特殊的父类对象!!!是一种 is-a的关系!

注意:父类的指针或引用可以通过强制类型转换赋值给子类的指针或引用,但是父类的指针是指向子类对象是才是安全的。(原因后续讲解

Student st;
Person pobj;
//父类指针指向子类对象再强转赋值是安全
Person* pp = &st;
Student* pst1 = (Student*)pp;//这种情况是可以的,安全

//不安全
Person* pp = &pobj;
Student* pst2 = (Student*)pp;//这种情况虽然可以,但是会存在越界访问的问题,不安全。

三、 继承中的作用域

  •  继承体系中,子类和父类都有独立的作用域
  • 子类和父类若存在同名成员(成员函数/成员变量),子类成员将屏蔽父类的同名成员,这种情况叫隐藏,也叫重定义。(在子类中若想访问父类的同名成员,可以使用父类::父类成员的方式访问)

同名成员变量隐藏

class Father
{
protected:
	int _num = 0;
};

//子类继承了父类Person
class Son : public Father
{
public:
	void func()
	{
		cout << _num << endl;//隐藏父类的同名成员,输出子类的成员,1

		cout << Father::_num << endl;//0
	}
protected:
	int _num = 1;
};

同名成员函数的隐藏 

class Father
{
public:
	void func()
	{
		cout << "Father func()" << endl;
	}
};
class Son : public Father
{
public:
	void func(int i)
	{
		cout << "Son func(int i)->" << i<< endl;
		Father::func();//类内调用
	}
};
int main()
{
	Son s;
	s.func(1);
	s.Father::func();//类外调用
	return 0;
}

①以上代码的父类和子类并不是重载关系,因为两个同名函数并不在同一个作用域!

②子类的func和父类的func构成隐藏,成员函数只要是同名就能构成隐藏!

因为独立作用域,所以相当于就近原则访问,一个全局一个局部,当然优先局部!

注意: 实际上在继承体系中最好不要有同名成员!!

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

默认成员函数,"默认"意思就是我们不写编译器自动生成一个,那么子类是如何生成的呢?

首先我们得先明确子类对象的模型,子类对象实际上包含了父类的成员变量+自己的成员变量

构造函数

由上述对象模型可以得出:父类在子类中就像是一个自定义类型!父类当成是一个整体!(复用

  •  父类成员变量,会调用父类自己的默认构造函数去初始化(复用)!如果父类没有合适的默认构造函数,则必须在子类构造函数的初始化列表阶段显示调用。 

显示调用

注意:若不想显示调用,那父类就必须提供默认构造函数(无参,全缺省,编译器自动生成)!!

如下:

  • 子类自己的成员变量,还是一样(类和对象一样),对内置类型不做处理,自定义类型还是会去调用对应的构造!

 注意:继承体系中构造的顺序是:先构造父类再构造子类!

析构函数 

  • 子类的析构函数会在调用完成之后自动调用父类的析构函数去清理父类的成员变量!所以我们一般不显示调用父类的析构函数!为了保证析构顺序!
  • 子类对象的析构顺序:先析构子类再析构父类!!

 演示:

可以看到,如果显示调用父类的析构,那么在程序结束时,又会自动调用父类的析构,万一父类的析构中存在释放申请的内存空间等问题,那必然会引起同一块空间连续多次释放的问题!同时这也不满足要求的析构顺序:先子后父!

为啥一定要先子后父?

因为如果显示写父类析构,那就会先析构父类,父类成员变量被清理,此时子类就不能访问了,万一子类要访问时,且该成员变量是指针,那必然会造成野指针问题!所以一定要遵循先子后父!不要在子类中显示调用父类的析构!

此外,子类能够自动调用的原因,那就是子类隐藏了父类的析构函数,实际上在类中的析构函数在编译器看来都是一个名字---destrutor(),在继承体系中,只要是同名函数,就能构成隐藏!!

拷贝构造/赋值重载 

  • 拷贝构造:和构造类似,父类必须调用父类的拷贝构造完成初始化
  • 赋值重载:operator=必须要调用基类的operator=完成基类的复制。

演示:

拷贝构造:

 

五、继承与友元

注意:友元关系不能继承,也就是父类友元不能访问子类私有和保护变量 !

理解:就好像你爸爸的朋友不是你的朋友一样!

class Father
{
public:
	friend void Show(const Father& p, const Son& s);
private:
	string _name;
};

class Son : public Father
{
protected:
	int _num;
	string _sex;
};

void Show(const Father& p, const Son& s)
{
	//父类的友元可以访问父类的私有
	cout << p._name << endl;

	//不能访问子类的私有/保护
	cout << s._num << endl;
}
int main()
{
	Father f;
	Son s;
	Show(f, s);
	return 0;
}

若想访问子类的私有变量,可以在子类中声明友元!

 六、继承与静态成员

父类定义了static静态成员,则整个继承体系里面只有一个这样的成员。也就是说父类的静态成员属于当前类,也属于当前父类的所有子类,都是共用一个static变量!

它的用处就是可以去统计父亲有多少个儿子!只需在父类的构造中++静态成员即可!


 好了,兄弟们,今天的分享就到这里,如果对你有帮助,欢迎三连!!你的支持是我前进的动力!

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

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

相关文章

Spring中是如何实现IoC和DI的?

前言&#xff1a;在前一篇文章中对于IoC的核心思想进行了讲解&#xff0c;而本篇文章则从Spring的角度入手&#xff0c;体会Spring对于IoC是如何实现的。 如果对IoC还有不太了解的可以阅读上一篇文章&#xff0c;相信一定会带来全新的收获&#xff1a;什么是IoC&#xff08;控制…

5.5软件工程-系统测试

系统测试 意义和目的原则测试过程测试策略测试方法练习题 测试用例设计黑盒测试等价类划分边界值分析错误推测因果图 白盒测试逻辑覆盖循环覆盖基本路径测试法 练习题 调试软件度量练习题 考点少&#xff0c;知识点多 意义和目的 系统测试的意义&#xff1a;系统测试是为了发现…

浅谈Redis集群架构与主从架构

目录 1. Redis集群1.1 集群概念1.2 集群分片1.3 重新分片 2. 集群的主从模型2.1 主从模型2.2 主节点选举 1. Redis集群 1.1 集群概念 面试官&#xff1a;我看你简历写了Redis集群&#xff0c;你说一说&#xff1f; Redis主从架构和Redis集群架构是两种不同的概念&#xff0c;大…

【Spring成神之路】从源码角度深度刨析Spring循环依赖

文章目录 一、引言二、循环依赖出现的场景2.1 有参构造导致的循环依赖问题2.2 属性注入出现的依赖问题2.3 Spring IOC创建Bean的流程2.4 有参构造为何失败2.5 属性注入为何能成功2.6 AOP导致的循环依赖 三、Spring循环依赖源码刨析四、Spring循环依赖案例刨析 一、引言 循环依…

【MATLAB源码】数学建模基础教程---初步认识数学建模

系列文章目录在最后面&#xff0c;各位同仁感兴趣可以看看&#xff01; 什么是数学建模 含义1.区分数学模型和数学建模2. 建立数学模型的注意事项3.数学建模流程图解4.数学建模模型分类5.论文常用套路6.最后&#xff1a;总结系列文章目录 含义 所谓数学建模&#xff0c;简言…

Python 中实现聊天客户端库

在 Python 中实现一个简单的聊天客户端库可以通过使用 socket 模块来处理网络通信。我们可以构建一个基于 TCP 的简单聊天系统&#xff0c;其中包括一个服务器和一个客户端。 1、问题背景 假设您正在尝试编写一个 Python 库&#xff0c;用于实现某个聊天协议的客户端。在连接…

c++入门基础(下篇)————引用、inline、nullptr

引用 引用的概念和定义 引⽤不是新定义⼀个变量&#xff0c;⽽是给已存在变量取了⼀个别名&#xff0c;编译器不会为引⽤变量开辟内存空间&#xff0c; 它和它引⽤的变量共⽤同⼀块内存空间。 类型& 引用别名 引用对象; 就像孙悟空也叫齐天大圣 猪八戒也叫天蓬元帅。…

正点原子imx6ull-mini-Linux驱动之Linux 自带的 LED 灯驱动实验(16)

前面我们都是自己编写 LED 灯驱动&#xff0c;其实像 LED 灯这样非常基础的设备驱动&#xff0c;Linux 内 核已经集成了。Linux 内核的 LED 灯驱动采用 platform 框架&#xff0c;因此我们只需要按照要求在设备 树文件中添加相应的 LED 节点即可&#xff0c;本章我们就来学习如…

Level3答案

突然发现&#xff0c;忘记公布了Level3答案&#xff1a; 1、 (1)heker.h HeiKe.h (2)Make_Text() (3)3 (4)heker.h 2、 (1)ArtText.h Maker_World.h (Maker_Game头文件组) (2)5.0 附加题、 我把标题截了张图&#xff01; 这是我们 Cookie Maker工作室 新出来的 “无标题技术”…

JavaScript基础——JavaScript数据及数据类型

JavaScript中数据的分类 数据是指设备、浏览器可以识别的内容。在JavaScript中&#xff0c;数据可分为基本数据类型&#xff08;值数据类型&#xff09;和引用数据类型。 console.log()函数 浏览器中按下F12或者右击检查&#xff0c;可以打开控制台。 在JavaScript中&#xff0…

微服务通过X-Forwarded-For获取客户端最原始的IP地址

文章目录 引言I 通过转发IP列表获取用户的IP地址II 存储真实IP字段到MDC中2.1 自己存储真实IP字段,方便获取。2.2 feign 传递MDC数据(将MDC中数据传入header)III 处理真实IP(应用)3.1 从MDC获取存储到日志系统中3.2 logback获取MDC数据(IP、追踪码)3.3 打印接口的请求IP引…

教你用python代码写一个中国象棋游戏

编写一个完整的中国象棋游戏是一个复杂的项目&#xff0c;因为它涉及到图形用户界面(GUI)的设计、游戏规则的实现、AI对手的开发等多个方面。不过&#xff0c;我可以提供一个简化的框架和一些基本思路&#xff0c;帮助你开始这个项目。 由于这里不能完整地实现一个图形化的象棋…

三十六、MyBatis-Plus(2)

&#x1f33b;&#x1f33b; 目录 一、CRUD 扩展&#xff08;1)1.1 Insert1.2 主键生成策略1.2.1 源码解释1.2.2 Twitter的snowflake算法 (雪花算法)1.2.3 主键自增&#xff1a;AUTO 我们需要配置主键自增1.2.4 手动输入&#xff1a;INPUT 就需要自己写 id 1.3 Update1.4 自动填…

2024杭电多校第五场

第一题&#xff1a;开关灯 直接暴力找规律。 发现如果n2&#xff08;mod3&#xff09;那么就是2的n-1次方。否则直接是2的n次方。 暴力代码 #include<bits/stdc.h> using namespace std; #define int long longsigned main() {int temp[100];temp[0] 1;for (int i …

SOMEIP_ETS_001:数组长度超过消息长度允许的范围

测试目的&#xff1a; 验证DUT&#xff08;Device Under Test&#xff0c;被测设备&#xff09;在接收到数组长度超过SOME/IP协议允许的最大长度时&#xff0c;是否能够返回错误消息。 描述 本测试用例旨在检查DUT在接收到一个SOME/IP消息时&#xff0c;如果该消息中的数组长…

Java学习:今日成果,明日挑战

阅读指南&#xff1a;[题目] - 精选摘要 题目1.面向对象编程意味着2.以下哪项不是 Java 关键字&#xff1f;3.基础数据类型在堆栈上分配&#xff1f;4.以下代码将导致&#xff1a;5.以下输出是什么 &#xff1f;6.如果我们声明&#xff1a;7.Java 使用按值调用。 以下方法调用传…

S7-1200PLC 和8块欧姆龙温控表MODBUS通信(完整SCL代码)

1、如何提升MODBUS-RTU通信数据的刷新速度 提升MODBUS-RTU通信数据刷新速度的常用方法_modbus rtu通讯慢-CSDN博客文章浏览阅读1.2k次。SMART PLC的MODBUS-RTU通信请参考下面文章链接:【精选】PLC MODBUS通信优化、提高通信效率避免权限冲突(程序+算法描述)-CSDN博客MODBU…

MATLAB预测模型(1)

一、前言 在MATLAB中&#xff0c;解决和预测微分方程通常涉及到使用数值方法&#xff0c;因为许多微分方程的解析解是难以找到的。MATLAB提供了多种函数和工具箱来处理这类问题&#xff0c;其中ode45是最常用的一个&#xff0c;用于求解非刚性微分方程的初值问题。 二、实现 以…

Linux中DHCP服务器配置和管理

文章目录 一、DHCP服务1.1、DHCP的工作流程1.2、DHCP的工作模式1.3、dhcp的主要配置文件 二、安装DHCP服务2.1、更新yum源2.2、安装DHCP服务软件包2.3、配置DHCP服务2.4、启用DHCP服务&#xff08;解决报错&#xff09;2.4.1、查看dhcpd服务的状态和最近的日志条目2.4.2、查看与…

代码随想录27天|贪心

455.分发饼干 代码随想录 第一想法 将孩子胃口值g[i] 按从小到达的顺序排列&#xff0c;饼干尺寸也按照从小到大的顺序去排列。 优先将大尺寸喂给大胃口孩子。如果满足不了胃口那么久试着分给下一个孩子。 要尽量满足更多的孩子&#xff0c;那么大尺寸的饼干就不能喂给小胃口…