【C++】继承---上(继承的引入及使用详解、切片赋值和作用域)

news2024/11/29 4:05:21

前言:

    我们在学习C++的第一节课就了解到C++是一门面向对象的语言,面向对象的语言有三大特性:

封装、继承、多态

    此前我们学习了封装,比如模拟实现vector,string或者迭代器等,不仅有利于我们的维护和管理,也可以降低用户的使用层本(如iterator将各个容器历遍访问的底层进行了封装,外部调用的形式是一样的,其实内部实现不一样)。

    同时面向对象不止三大特性,还有其他特性,例如:反射(C++没有,java有),抽象。

本文将介绍面向对象编程的另一大特性:继承。

继承是在更高的维度来设计类。

(一)继承的概念和定义

 (1)继承的引入

假如我们要实现一个图书管理系统,我们可能要封装学生类,教师类等:

 我们发现:

  • 有的数据和方法(函数)是这两个角色共有的------重复设计了
  • 有的数据和方法(函数)是每个角色独有的

我们写代码讲究的是复用

例如,我们多个成员要重复调用同一段代码,我们会封装一个函数以来调用,以达到复用的效果;

再如,我们模拟实现如vector等容器时,top等接口函数需要返回const类型的值和普通类型的值,但其实内部实现一样,我们会想办法用一个模板参数来传参区分.......

所以为了要解决上述的问题,C++用继承的方式对共同的类进行提取。

(2)继承的概念及定义

概念:

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

 上面我们看到Person是父类,也称作基类。Student(Teacher)是子类,也称作派生类。

 (3)继承的方式

我们注意到上面学生类的写法是class Student: public Person。这里的public就是一种继承的方式。

C++给出了三种继承的方式:

之前我们学习过访问限定符和这里很相似:

 

 父类成员有三种访问限定符,子类继承父类又有三种继承方式,两两一组合,所以组合出来就有9种组合方式!

继承基类成员访问方式的变化:

 

总结:
  • 1. 基类private成员在派生类中无论以什么方式继承都是不可见的。这里的不可见是指基类的私 有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面 都不能去访问它
  • 2. 基类private成员在派生类中是不能被访问,如果基类成员不想在类外直接被访问,但需要在 派生类中能访问,就定义为protected。可以看出保护成员限定符是因继承才出现的
  • 3. 实际上面的表格我们进行一下总结会发现,基类的私有成员在子类都是不可见。基类的其他 成员在子类的访问方式 == Min(成员在基类的访问限定符,继承方式),public > protected > private。
  • 4. 使用关键字class时默认的继承方式是private,使用struct时默认的继承方式是public,不过 最好显示的写出继承方式
  • 5. 在实际运用中一般使用都是public继承,几乎很少使用protetced/private继承,也不提倡 使用protetced/private继承,因为protetced/private继承下来的成员都只能在派生类的类里 面使用,实际中扩展维护性不强 。

总结提炼:

  • 先将基类的 私有成员 公有/保护成员 分成两类去看待
  • 基类的私有成员在子类都是不可见
  • 基类中其他成员的访问方式是 (成员在基类的访问限定符,继承方式)当中,权限小的那一个
  • 权限关系我们可以认为是:public > protected > private

 父类的私有成员也会被继承过来,只是语法限制其不能访问。
 不可见意思是:在类外面和类里面都不能访问。
 之前我们学的,保护和私有 都是在类外面不能访问,在类里面可以访问,没有区别
 但是在继承这里是有区别的,如果父类的成员是保护(protected)且继承关系不是private,子类可以用,如果父类成员是私有(private)子类不能用。

证明父类私有成员确实被继承下来的程序:



class Person
{
protected:
	//private:

	string _name; //姓名
	string _tel; //电话
	string _address; //地址
};

class Student : public Person
{
protected:

	int _age;
	string _stuId;
};

int main()
{
	Student s;
	cout << sizeof(s) << endl;

	return 0;

无论父类成员是 私有(private)还是 保护(protected)子类的大小都是一样的 --> 116

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

派生类对象 可以赋值给 基类的对象 / 基类的指针 / 基类的引用
  • 这里有个形象的说法叫切片 (或者切割)。寓意把派生类中父类那部分切来赋值过去
  • 基类对象不能赋值给派生类对象。
  • 基类的指针或者引用可以通过强制类型转换赋值给派生类的指针或者引用。但是必须是基类 的指针是指向派生类对象时才是安全的。这里基类如果是多态类型,可以使用RTTI(Run-Time Type Information)的dynamic_cast 来进行识别后进行安全转换。(ps:这个我们后 面再讲解,这里先了解一下)

 这个过程是天然支持的没有类型转换。

切片举例:

class Person
{
public:
	string _name; //姓名
	string _tel;  //电话
	string _address; //地址

	int _age; //年龄
};

class Student : public Person
{
	string _stuId;
};

class Teacher : public Person
{
	string _workId;
};

int main()
{
	Person p;
	Student s;
	s._name = "张三";
	s._tel = "12345";
	s._address = "学校";

	p = s;
	Person& rp = s;
	Person* ptr = &s;

	return 0;
}

对象赋值切片可以理解为是将值依次赋值过去。


如何理解引用和指针的切片?

  • rp是子类当中,父类那一部分的别名
  • 指针是指向子类中父类的那一部分,但是指针类型向后看的大小是切出来的一部分

并且这三者地址是一样的:

 

 

 

赋值兼容的规则是建立在 public(公有)继承的基础上

  • 因为成员变量在继承下来时,可能已经变成了保护的或者是私有的
  • 赋值给父类对象有可能就变成父类的公有成员了,这样不太合理


 父类/基类 对象赋值给 子类/派生类 对象是不可以的

  • 只能向上去赋值(切片),不能向下赋值
  • 也就是说子能给父,父不能给子
  • 通过强转的话,对象不能强转,基类/父类 的指针可以强转成 子类/派生类的指针 或者是引用也可以(过程涉及到dynamic_cast了解 – 这样才安全),以后学习会讲到
  • 实际当中很少遇到父类转子类,基本上都是子类转父类。

(三)继承中的作用域

  • 1. 在继承体系中基类派生类都有独立的作用域
  • 2. 子类和父类中有同名成员,子类成员将屏蔽父类对同名成员的直接访问,这种情况叫隐藏, 也叫重定义。(在子类成员函数中,可以使用 基类::基类成员 显示访问
  • 3. 需要注意的是如果是成员函数的隐藏,只需要函数名相同就构成隐藏。
  • 4. 注意在实际中在继承体系里面最好不要定义同名的成员

成员变量同名的隐藏:

class Person
{
protected:
	string _name = "小李子";
	int _num = 111;			
};

class Student : public Person
{
public:
	void Print()
	{
		cout << "姓名:" << _name << endl;
		cout << "子类的身份证号:" << _num << endl; 

		//告诉编译器直接去父类中找
		cout << "父亲类的身份证号:" << Person::_num << endl;

	}
protected:
	int _num = 999; 
};

int main()
{
	Student s1;
	s1.Print();

	return 0;
};

s1中有两个_num,一个是继承父类的,一个是自己的:

成员函数同名的隐藏: 

//成员函数同名 -- 两个func的关系是隐藏
class A
{
public:
	void func()
	{
		cout << "A::func()" << endl;
	}
};

class B : public A
{
public:
	void func()
	{
		cout << "B::func()" << endl;
	}
};

int main()
{
	B b;
	b.func();

	//指定作用域去调用
	b.A::func();

	return 0;
}

注意:

  • A::fun 和 B::fun 的关系是 -> 隐藏
  • 函数重载要求在同一个作用域,在不同的作用域不是重载
  • 所以如果是成员函数的隐藏,只需要函数名相同就构成了隐藏

建议:
在继承里面,成员变量和成员函数最好都不要定义成同名的。

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

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

相关文章

【uniapp】Vue3版本项目出现Proxy代理对象无法正常取值问题解决

习惯了使用Vue2版本的uniapp项目&#xff0c;直到后来想升级版本&#xff0c;改用Vue3版本就会遇到一些无法正常取值&#xff08;访问属性&#xff09;问题&#xff0c;其中最显著问题就是Proxy代理造成的 在我们用浏览器调试的时候&#xff0c;遇到如同下图这样&#xff0c;出…

Spring Boot 项目如何实现上传头像功能?

目录 设计思路 效果展示 ​编辑 分析 前后端交互接口 请求 响应 代码实现和详细注释 数据库设计 自定义资源映射 前后端交互 客户端开发 服务器开发 设计思路 效果展示 分析 实现这个功能只要弄清楚以下几点即可&#xff1a; 怎么将头像数据发送给服务器&#x…

九龙证券|服务器龙头获资金连续抢筹,尾盘主力抢筹前期大热门股

今天&#xff0c;核算机职业取得主力大手笔抢筹。 今天主力资金净流出53.89亿元&#xff0c;其间创业板净流出3.19亿元&#xff0c;沪深300成份股净流出7.61亿元。 申万一级职业中&#xff0c;今天有19个职业上涨&#xff0c;传媒职业接连两日均涨近5%&#xff0c;居首位&…

stm32当中GPIO输出知识点汇总(GPIO的八种模式及其原理)

一、GPIO工作模式. 1. 四种输入模式 GPIO_Mode_IN_FLOATING 浮空输入模式 GPIO_Mode_IPU 上拉输入模式 GPIO_Mode_IPD 下拉输入模式 GPIO_Mode_AIN 模拟输入模式 2. 四种输出模式 GPIO_Mode_Out_OD 开漏输出模式 GPIO_Mode_Out_PP 推挽输出模式 GPIO_Mod…

【剑指offer-C++】JZ79:判断是不是平衡二叉树

【剑指offer-C】JZ79&#xff1a;判断是不是平衡二叉树题目描述解题思路题目描述 描述&#xff1a;输入一棵节点数为 n 二叉树&#xff0c;判断该二叉树是否是平衡二叉树。 在这里&#xff0c;我们只需要考虑其平衡性&#xff0c;不需要考虑其是不是排序二叉树。 平衡二叉树…

Nginx实现负载均衡的多种方法演示

文章目录前言一、配置讲解1.1 轮询算法&#xff08;默认&#xff09;1.2 IP_HASH算法1.3 Weighted算法1.4 URL_HASH算法总结前言 Nginx是一款高性能的Web服务器和反向代理服务器,它具有占用内存小、并发处理能力强、稳定性高等优点&#xff0c;适用于高并发、高负载的Web应用场…

pandas之DataFrame基础

pandas之DataFrame基础1. DataFrame定义2. DataFrame的创建形式3. DataFrame的属性4. DataFrame的运算5. pandas访问相关操作5.1 使用 loc[]显示访问5.2 iloc[] 隐式访问5.3 总结6. 单层索引和多层级索引6.1 索引种类与使用6.2 索引相关设置6.3 索引构造6.4 索引访问6.5 索引变…

BGP选路实验(重点是各种策略)

实验拓扑 基础配置(完成IGP的配置) 首先完成各 个接口IP地址,环回接口地址等一些基本配置&#xff0c;实现直连之间的互相通信 在R4&#xff0c;R5上的配置类似 [Huawei]int g0/0/0 [Huawei-GigabitEthernet0/0/0]ip add 192.168.1.1 24 [Huawei-GigabitEthernet0/0/0]int g0/0…

低代码是什么意思

此前&#xff0c;阿里云智能总裁张建锋曾在钉钉发布会上表示&#xff1a;“未来的软件开发一定是碎片化的&#xff0c;低代码开发将成为未来几年的行业关键词。”这句话表明了低代码在过去两年的火爆程度&#xff0c;预示着低代码有望成为软件领域的新风口。 那低代码是什么意…

chatgpt智能提效职场办公-excel表格6-6-6格式怎么设置(数字按照三个数字一组进行分隔)

chatgpt智能提效职场办公-excel表格6-6-6格式怎么设置&#xff08;数字按照三个数字一组进行分隔&#xff09; 作者&#xff1a;虚坏叔叔 博客&#xff1a;https://xuhss.com 早餐店不会开到晚上&#xff0c;想吃的人早就来了&#xff01;&#x1f604; 在Excel表格中设置6-6-…

移除元素(数组篇)

27. 移除元素 给你一个数组 nums 和一个值 val&#xff0c;你需要 原地 移除所有数值等于 val 的元素&#xff0c;并返回移除后数组的新长度。 不要使用额外的数组空间&#xff0c;你必须仅使用 O(1) 额外空间并 原地 修改输入数组。 元素的顺序可以改变。你不需要考虑数组中…

004:Mapbox GL设定不同的style,更换底图形态

第004个 点击查看专栏目录 本示例的目的是介绍演示如何在vue+mapbox中设定不同的style,更换底图形态 。 直接复制下面的 vue+mapbox源代码,操作2分钟即可运行实现效果 文章目录 示例效果配置方式示例源代码(共70行)相关API参考:专栏目标示例效果

「业务架构」需求工程—需求规范(第3部分)

将用户和系统需求记录到文档中。需求规范它是将用户和系统需求写入文档的过程。需求应该是清晰的、容易理解的、完整的和一致的。在实践中&#xff0c;这是很难实现的&#xff0c;因为涉众以不同的方式解释需求&#xff0c;并且在需求中经常存在固有的冲突和不一致。正如我们之…

GraphInsight 拓扑图体验

蚂蚁数据可视化 | G6&#x1f4da;前言&#x1f4da;使用说明&#x1f4d5;数据规则节点的属性&#x1f4da;前言 蚂蚁数据可视化 G6 图可视化引擎是一个简单、易用、完备的图可视化引擎&#xff0c;它在高定制能力的基础上&#xff0c;提供了一系列设计优雅、便于使用的图可视…

1~3年的测试工程师薪资陷入了瓶颈期,如何突破自己实现涨薪?

对于技术人员而言&#xff0c;职业规划一般分为两个方向&#xff1a;做技术、做管理。进入软件测试行业的新人都会从最基础的执行开始&#xff0c;然后是基本的功能测试。 随后大家会根据个人职业发展来进一步细化&#xff0c;有的走管理路线&#xff0c;成为主管、经理、项目…

《操作系统》by李治军 | 实验3 - 系统调用

目录 一、实验内容 二、实验准备 1、系统调用的具体流程 &#xff08;一&#xff09;调用接口函数 API &#xff08;二&#xff09;触发 0x80 号中断 &#xff08;三&#xff09;跳转到 system_call 函数 &#xff08;四&#xff09;执行系统调用函数 sys_xxx 2、总结概…

19从零开始学Java之局部变量和成员变量是怎么回事?

作者&#xff1a;孙玉昌&#xff0c;昵称【一一哥】&#xff0c;另外【壹壹哥】也是我哦 千锋教育高级教研员、CSDN博客专家、万粉博主、阿里云专家博主、掘金优质作者 前言 在前两篇文章中&#xff0c;壹哥给大家讲解了Java里的条件分支&#xff0c;包括if和switch两种情况。…

23.Spring练习(spring、springMVC)

目录 一、Spring练习环境搭建。 &#xff08;1&#xff09;设置服务器启动的展示页面。 &#xff08;2&#xff09;创建工程步骤。 &#xff08;3&#xff09;applicationContext.xml配置文件。 &#xff08;4&#xff09;spring-mvc.xml配置文件。 &#xff08;5&#x…

Java集合——Set接口学习总结

一、HashSet实现类 1.常用方法 增加&#xff1a;add(E e)删除&#xff1a;remove(Object o)、clear()修改&#xff1a;查看&#xff1a;iterator()判断&#xff1a;contains(Object o)、isEmpty()常用遍历方式&#xff1a;Set<String> set new HashSet<String>()…

redis中布隆过滤器使用详解

文章目录一、布隆过滤器介绍1、什么是布隆过滤器2、布隆过滤器实现原理3、误判率4、布隆过滤器使用场景5、哈希表与布隆过滤器比较二、redis中布隆过滤器实战1.引入redisson依赖2.创建订单表3.配置redis4.配置BloomFilter5.创建订单6.单元测试总结一、布隆过滤器介绍 1、什么是…