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

前言:

    我们在学习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.kler.cn/a/9323.html

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

相关文章

如何选择适合的企业网站建站方案?

企业网站建设是企业发展的重要组成部分&#xff0c;一份好的网站可以为企业带来更多的流量、更高的曝光度和更好的品牌形象&#xff0c;因此如何选择适合的企业网站建站方案就显得尤为重要。 那么&#xff0c;如何选择适合的企业网站建站方案呢&#xff1f;下面以一家小型企业…

【Linux】基础IO

基础文件IOC库的文件接口系统调用的文件接口文件描述符和文件流指针的关系重定向C库的文件接口 在c语言中&#xff0c;会使用fopen&#xff0c;fclose&#xff0c;fseek&#xff0c;fread&#xff0c;fwrite等函数进行文件IO的操作 以fopen函数举例 //FILE*表示文件流指针&am…

【微信小程序】-- 自定义组件 - 父子组件之间的通信(三十八)

&#x1f48c; 所属专栏&#xff1a;【微信小程序开发教程】 &#x1f600; 作  者&#xff1a;我是夜阑的狗&#x1f436; &#x1f680; 个人简介&#xff1a;一个正在努力学技术的CV工程师&#xff0c;专注基础和实战分享 &#xff0c;欢迎咨询&#xff01; &…

Flutter 生命周期原理

一 这里看一下StatefulWidget的生命周期 其本身是由两个类组成的&#xff0c;StatefulWidget 和 State 组成的。 class DemoWidget extends StatefulWidget {const DemoWidget({super.key});overrideState<DemoWidget> createState() > _DemoWidgetState(); }class _…

Properties

Properties概述&#xff1a; 是一个Map体系的集合类 Properties可以保存到流中或从流中加载 练习&#xff1a;Properties作为Map集合的使用 package com.aynu13;//练习&#xff1a;Properties作为Map集合的使用import java.util.Properties; import java.util.Set;public cla…

单机最快的队列Disruptor解析和使用

前言 介绍高性能队列Disruptor原理以及使用例子。 Disruptor是什么? Disruptor是外汇和加密货币交易所运营商 LMAX group 建立高性能的金融交易所的结果。用于解决生产者、消费者及其数据存储的设计问题的高性能队列实现。可以对标JDK中的ArrayBlockingQueue。是目前单机且…

【JavaWeb】1—JavaWeb概述

⭐⭐⭐⭐⭐⭐ Github主页&#x1f449;https://github.com/A-BigTree 笔记链接&#x1f449;https://github.com/A-BigTree/Code_Learning ⭐⭐⭐⭐⭐⭐ 如果可以&#xff0c;麻烦各位看官顺手点个star~&#x1f60a; 如果文章对你有所帮助&#xff0c;可以点赞&#x1f44d;…

人工智能中的移动端编程

移动端编程是现在新兴的主要编程领域之一&#xff0c;该领域聚集了非常多的开发人员。这主要得益于手机和平板电脑的快速普及&#xff0c;人们以前需要在台式机上完成的事情&#xff0c;现在都可以非常方便地在手机或平板电脑上完成。由于手机和平板电脑携带更加方便&#xff0…

阿里云版GPT官宣,我们问了它10个问题

4月7日&#xff0c;阿里云宣布自研大模型“通义千问”&#xff0c;目前已开始邀请用户测试体验。 阿里达摩院在NLP自然语言处理等前沿科研领域早已布局多年&#xff0c;并于2019年启动大模型研发&#xff0c;通义千问便是其最新成果&#xff0c;相当于阿里云版的“ChatGPT”。 …

网络编程之输入ip地址解析不出来域名

网络编程之输入ip地址解析不出来域名 1.解决方案 设置本机的域名解析服务器 1. 查看域名的ip ping 域名 找到如下图路径下的hosts文件 赋予权限 添加域名和ip地址的对应关系。域名和ip之间采用空格隔开。 代码测试 代码详见&#xff1a;网络编程---实验2 查找Internet地址和用…

腾讯云轻量应用服务器16核32G28M处理器带宽流量性能测评

腾讯云轻量应用服务器16核32G28M带宽&#xff0c;28M带宽下载速度峰值可达3584KB/s&#xff0c;折合3.5M/秒&#xff0c;16核32G28M带宽3468元15个月&#xff0c;折合每月231元&#xff0c;系统盘为380GB SSD盘&#xff0c;免费6000GB月流量&#xff0c;折合每天200GB流量&…

系统集成项目管理工程师案例分析考点汇总(成本、质量、人力)

项目成本管理常见考点1. 成本估算、成本预算的步骤2. 成本估算、成本预算的区别与联系3. 成本估算困难或不准的原因4. 成本失控的原因5. 成本超支、进度落后采取的措施6. 成本超支、进度超前采取的措施项目质量管理常见考点1. 质量管理计划的内容2. 质量保证与质量控制的联系3.…

「解析」Matplotlib 绘制折线图

相比于【优雅】matplotlib 常见图、【优雅】matplotlib 3D图 而言&#xff0c;折线图使用的频率会更高一些&#xff0c;在此整理下最近使用 Matplotlib 绘制折线图常用的一些配置&#xff0c;小伙伴们只需要修改对应的 aug_list、list 即可直接使用 # !/usr/bin/env python …

在线Plist文件格式转Json文件格式

Plist文件是一种用于存储应用程序配置信息的文件格式&#xff0c;其中包含应用程序的各种设置和数据。在过去&#xff0c;Plist文件通常是以 .plist 格式存储的。然而&#xff0c;随着时间的推移&#xff0c;人们开始使用 JSON 格式来存储更复杂的数据结构和数据。如果您需要将…

77-Linux_网络编程

网络编程一.主机字节序列和网络字节序列二.套接字地址结构1.通用socket地址结构2.专用的socket地址结构3.IP地址转换函数一.主机字节序列和网络字节序列 主机字节序列分为大端字节序和小端字节序&#xff0c;不同的主机采用的字节序列可能不同。 大端字节序是指一个整数的高位…

二 、Locust自定义用户(场景)

二 、自定义用户&#xff08;场景&#xff09; 一个用户类代表了你系统中的一种用户/场景。当你做一个测试运行时&#xff0c;你指定你想模拟的并发用户的数量&#xff0c;Locust将为每个用户创建一个实例。你可以给这些类/实例添加任何你喜欢的属性&#xff0c;但有一些属性对…

shell 脚本编写

文章目录练习题目&#xff1a;1.编写函数&#xff0c;实现打印绿色0K和红色FAILED,判断是否有参数&#xff0c;存在为0k&#xff0c;不存在为FAILED2.编写函数&#xff0c;实现判断是否无位置参数&#xff0c;如无参数&#xff0c;提示错误3.编写函数实现两个数字做为参数&…

uniapp - 实现车牌号键盘与格子间隔显示组件,汽车牌照录入支持自定义样式、新能源等(附带组件完整源码,开箱即用,稍微改改就能用)

效果图 uniapp 全平台兼容,车牌号键盘输入、分格显示功能示例源码,注释很多! 可以直接复制一下,然后自己改改样式或功能就能使了。 示例源码 复制,运行。 &

ReRes 谷歌浏览器插件使用

安装&#xff1a; 本插件是chrome插件&#xff0c;有条件者可以直接在chrome商店下载安装&#xff1b; 条件有限者&#xff1a; CSDN 下载 ReRes找到插件的github路径&#xff0c;本文插件点击 ReRes下载到本地并解压在chrome浏览器地址栏输入chrome://extensions/进入扩展页…
最新文章