C++ 继承(1)

news2025/2/11 0:07:54

1.继承概念

我们平时有时候在写多个有内容重复的类的时候会很麻烦

比如我要写Student Teacher Staff 这三个类

里面都要包含  sex  name age成员变量

唯一不同的可能有一个成员变量 但是这三个成员变量我要写三遍  

太麻烦了 有没有好的方式呢?

有的 就是继承、我们先来看继承的书写格式

但是我们继承过来的类里面的成员是public  private 还是protected呢?

在此之前我们要了解一下继承方式和访问限定符有哪些

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

这个地方很好记忆 记住两点就可以了

 ***这里的不可见要和private区分开来

***简单点说就是基类访问方式和派生类的继承方式的最小方式 

除此之外还有我们之前学类成员函数没有了解到的protected

 虽然struct和class有默认继承方式,但是最好还是显示写

当然以上这么多看起来好麻烦 但是实际让我们用的很少

2.基类和派生类类型转换

基类赋值给派生类是向下转换

派生类赋值给基类是向上转换

1.对于类成员,首先基类不能赋值给派生类(强制类型转换也不可以)

因为派生类有基类没有的成员变量 怎么赋值 

2.此外派生类赋值给基类时,既不是隐式类型转换 也不是强制类型转换

而是赋值兼容(也称切割,切片) 因此不会产生临时变量 不具有常性

把派生类中的基类部分切割出来 赋值给基类

class Person
{
	
};

class Student :public Person
{

};

int main(void)
{
	int i = 0;
	double& d = i;//隐式类型会产生临时变量 具有常性 报错
	Person p;
	Student s;
	p = s;
	Person& pp = s;//赋值兼容 不产生临时变量 不具有常性 不报错
	return 0;
}

像上面代码的变量pp 它是把派生类中的基类部分切割出来取别名

像上转换都是可以的

派生类赋值给 基类对象/引用/指针

至于向下转换

对象是不支持的

但是指针和引用可以

基类的指针/引用可以指向派生类中基类的那一部分

后面再详细讲解 

3.继承中的作用域

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

那么基类和派生类如果有同名的成员呢?

当然是可以 在不同的作用域里面可以有同名的函数

就像stack里面有push

queue里面也有push啊!

C++对于这种同名的成员变量或者函数取了一个名称 叫隐藏也叫重定义

这个地方函数的同名要和函数重载区分一下

首先函数重载是在同一个域中

这个地方的函数同名是处于两个不同的域

我们直接上代码就很容易理解了

class Person
{
public:
	void func()
	{
		cout << "基类" << endl;
	}
};

class Student :public Person
{
public:
	void func(int)
	{
		cout << "派生类" << endl;
	}
};
int main(void)
{
	Person p;
	Student s;
	Person& pp = s;
	s.func();//错误
	s.func(2);//正确
	s.Person::func();//正确
	return 0;
}

只要派生类和基类函数名相同就构成隐藏

总结

4.派生类的默认成员函数 

我们的派生类变量   中基类变量那部分   初始化会调用基类的构造函数

不可以在  派生类的初始化列表初始化   但是可以在派生类的构造函数函数体内使用

class Person
{
public:
	Person(int a)
	{
		cout << "基类" << endl;
	}
public:string sex;
};

class Student :public Person
{
public:
	Student()
		:age(18)
		,Person(2)//不加就会报错
	{
		sex = 12;
		cout << sex << endl;
	}
private:int age = 18;
};
int main(void)
{
	Student s;
		return 0;
}

 不加,Person(2)这个地方报错

这个地方你会发现先初始化Person后初始化age 因为初始化的顺序不是按照初始化列表的顺序

而是按照声明的顺序

按照声明来说  继承的类是在派生类自己类变量前面的

析构函数,拷贝构造和赋值重载也是差不多 但是还是有一些细微的差别

本质上相同!

​
​
class Person
{
public:
	Person(string a="Tony")
	{
		cout << "基类" << endl;
	}
	Person(const  Person& v)
		:sex(v.sex)
	{
	}
	Person& operator=(Person& v)
	{
		if (this != &v)
		{
			v.sex = sex;
		}
		return*this;
	}
	~Person()
	{

	}
public:string sex;
};

class Student :public Person
{
public:
	Student(string sex="zhangsan1")
		:age(18)
		,Person(sex)
	{
	}
	Student(const Student&s)
		:age(s.age)
		,Person(s)//Person拷贝构造函数要求Person类型的对象
		//这个地方我们没有Person对象  但是也可以直接传
		//派生类对象变量可以传给基类对象(向上转换 切割)
		//
		//这个地方Person(s)你不写编译器就会去调用Person的默认构造函数
	{

	}
	Student& operator=(Student& v)
	{
		if (this != &v)
		{
			Person::operator=(v);//这个地方就涉及到我们前面学习的隐藏了
			age = v.age;
		}
		return *this;
	}
	~Student()
	{
		//Person:~Person	由于后面多态的原因,析构函数的函数名被特殊处理了,
		// 统一处理成destructor了 这个时候就构成隐藏了
		//但是实际上这个地方不用我们显示写 因为编译器会自动调用
		//这个地方是先析构派生类自己的成员 再去析构派生类中基类的那部分成员
		//原因是派生类中可能会用到基类的成员  
		// 比如cout<<sex<<endl;
		}
private:int age = 18;
};

​

​

5.继承与友元

很简单 一句话

派生类不能继承基类的友元

6.继承与静态成员 

我们来看这一串代码

打印的地址相同说明派生类和基类使用的是同一个 

这个地方的静态成员属于父类和派生类

在派生类中不会单独拷贝一份、继承的是使用权

当然我们使用静态变量 无论是域名或者对象都可以! 

7.复杂的菱形继承及虚拟继承

继承是可以多继承的

但是多继承就容易出现问题

比如菱形继承

 这个时候就会出现两个问题

1.冗余性

我把同样的成员变量拷贝了两份,太冗余了

2.二义性

比如上图的Student类和Teacher类都要age成员的情况下

Assitant a;  a.age的这个age是从Teacher类继承过来的还是从Student继承过来的?

首先  解决二重性的方式比较简单 

我要用Tearher继承来的age就直接 a.Teacher::age;

Tearher继承来的age就直接 a.Student::age;

那么怎么解决冗余性呢?

这就涉及到了虚拟菱形继承了

虚拟菱形继承就是在普通继承前面+一个virtual!

class D : public virtual B, public virtual C
{
}

我们先来看普通菱形继承是怎么样的?

普通的菱形继承就和我们前面说的一样

有冗余 A类的成员被储存了两次 

我们再来看菱形虚拟继承

它把原本B类和C类共有继承的A类 单独 拿出来了 

但是这个地方多了两个东西 多了两个指针

指向的内容是距离A的偏移量(相对距离)

这也就意味着我们在使用B类和C类指针的时候

可以根据指针找到偏移量找到A的位置 毕竟A是B和C的基类

但是这个时候它们就没有存放到B和C里面

那么我为为什么不直接存偏移量在B和C里面 要存一个指针去找呢?

我们看到那个偏移量是存在第二个位置

第一个位置是为其他的预留位置

而且万一是三个值 四个值呢?

那我不是要存三四个地址?

那B C的大小会变得很大

而且万一你有其他D对象呢

那你岂不是要把所有的内容再存一遍?

直接用指针指向同一块公共区域不香吗?

毕竟这个偏移量只要是D对象都是固定的啊!

 这个时候B类的结构也发生了变化

偏移量和在D类对象的不一样

这个时候就容易出问题 尤其是切片的时候

我们再来看下面这串代码 

编译器单ptr->指向是无法判断ptr指向的是B类型的_a++还是D类型的

这个时候编译器就只能通过指针取到偏移量再根据偏移量去计算_aa的位置

上面两个ptr->_a++;本质上在编译的时候都是一样的

切片不需要特殊处理,都是通过指针去取偏移量 

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

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

相关文章

【C语言】传值调用与传址调用详解

博客主页&#xff1a; [小ᶻ☡꙳ᵃⁱᵍᶜ꙳] 本文专栏: C语言 文章目录 &#x1f4af;前言&#x1f4af;传值调用1. 什么是传值调用&#xff1f;2. 示例代码&#xff1a;传值调用失败的情况执行结果&#xff1a; 3. 为什么传值调用无法修改外部变量&#xff1f; &#x1f4…

蓝桥杯C语言组:图论问题

蓝桥杯C语言组图论问题研究 摘要 图论是计算机科学中的一个重要分支&#xff0c;在蓝桥杯C语言组竞赛中&#xff0c;图论问题频繁出现&#xff0c;对参赛选手的算法设计和编程能力提出了较高要求。本文系统地介绍了图论的基本概念、常见算法及其在蓝桥杯C语言组中的应用&#…

windows通过网络向Ubuntu发送文件/目录

由于最近要使用树莓派进行一些代码练习&#xff0c;但是好多东西都在windows里或虚拟机上&#xff0c;就想将文件传输到树莓派上&#xff0c;但试了发现u盘不能简单传送&#xff0c;就在网络上找到了通过windows 的scp命令传送 前提是树莓派先开启ssh服务&#xff0c;且Window…

Unity抖音云启动测试:如何用cmd命令行启动exe

相关资料&#xff1a;弹幕云启动&#xff08;原“玩法云启动能力”&#xff09;_直播小玩法_抖音开放平台 1&#xff0c;操作方法 在做云启动的时候&#xff0c;接完发现需要命令行模拟云环境测试启动&#xff0c;所以研究了下。 首先进入cmd命令&#xff0c;CD进入对应包的文件…

ZU47DR 100G光纤 高性能板卡

简介 2347DR是一款最大可提供8路ADC接收和8路DAC发射通道的高性能板卡。板卡选用高性价比的Xilinx的Zynq UltraScale RFSoC系列中XCZU47DR-FFVE1156作为处理芯片&#xff08;管脚可以兼容XCZU48DR-FFVE1156&#xff0c;主要差别在有无FEC&#xff08;信道纠错编解码&#xff0…

【算法】动态规划专题⑥ —— 完全背包问题 python

目录 前置知识进入正题模板 前置知识 【算法】动态规划专题⑤ —— 0-1背包问题 滚动数组优化 完全背包问题是动态规划中的一种经典问题&#xff0c;它与0-1背包问题相似&#xff0c;但有一个关键的区别&#xff1a;在完全背包问题中&#xff0c;每种物品都有无限的数量可用。…

C#中深度解析BinaryFormatter序列化生成的二进制文件

C#中深度解析BinaryFormatter序列化生成的二进制文件 BinaryFormatter序列化时,对象必须有 可序列化特性[Serializable] 一.新建窗体测试程序BinaryDeepAnalysisDemo,将默认的Form1重命名为FormBinaryDeepAnalysis 二.新建测试类Test Test.cs源程序如下: using System; us…

51单片机之引脚图(详解)

8051单片机引脚分类与功能笔记 1. 电源引脚 VCC&#xff08;第40脚&#xff09;&#xff1a;接入5V电源&#xff0c;为单片机提供工作电压。GND&#xff08;第20脚&#xff09;&#xff1a;接地端&#xff0c;确保电路的电位参考点。 2.时钟引脚 XTAL1&#xff08;第19脚&a…

jupyterLab插件开发

jupyter lab安装、配置&#xff1a; jupyter lab安装、配置教程_容器里装jupyterlab-CSDN博客 『Linux笔记』服务器搭建神器JupyterLab_linux_布衣小张-腾讯云开发者社区 Jupyter Lab | 安装、配置、插件推荐、多用户使用教程-腾讯云开发者社区-腾讯云 jupyterLab插件开发教…

配置#include “nlohmann/json.hpp“,用于处理json文件

#include “nlohmann/json.hpp” // 需要安装 nlohmann/json.hpp 头文件 using json = nlohmann::json; 下载链接:https://github.com/nlohmann/json/tree/develop 1.下载并解压:首先,需要从nlohmann/json的GitHub仓库下载源代码,并解压得到的文件。 地址: nlohmann/json…

MATLAB | 基于Theil-Sen斜率和Mann-Kendall检验的栅格数据趋势分析

最近看到一些博主分享关于 SenMK 检验的代码&#xff0c;对于新手来说可能有点复杂。我们编写了一段 MATLAB 代码&#xff0c;能够一次性解决这些问题&#xff0c;简化操作流程。我们还准备了几个关于趋势检验的空间分布图&#xff0c;供大家参考。 一、Sens Slope和Mann-Kenda…

Windows 系统下使用 Ollama 离线部署 DeepSeek - R1 模型指南

引言 随着人工智能技术的飞速发展&#xff0c;各类大语言模型层出不穷。DeepSeek - R1 凭借其出色的语言理解和生成能力&#xff0c;受到了广泛关注。而 Ollama 作为一款便捷的模型管理和部署工具&#xff0c;能够帮助我们轻松地在本地环境中部署和使用模型。本文将详细介绍如…

Docker、Ollama、Dify 及 DeepSeek 安装配置与搭建企业级本地私有化知识库实践

在现代企业中&#xff0c;管理和快速访问知识库是提升工作效率、促进创新的关键。为了满足这些需求&#xff0c;企业越来越倾向于构建本地私有化的知识库系统&#xff0c;这样可以更好地保护企业数据的安全性和隐私性。本文将介绍如何利用 **Docker**、**Ollama**、**Dify** 和…

【漫话机器学习系列】087.常见的神经网络最优化算法(Common Optimizers Of Neural Nets)

常见的神经网络优化算法 1. 引言 在深度学习中&#xff0c;优化算法&#xff08;Optimizers&#xff09;用于更新神经网络的权重&#xff0c;以最小化损失函数&#xff08;Loss Function&#xff09;。一个高效的优化算法可以加速训练过程&#xff0c;并提高模型的性能和稳定…

【JVM详解四】执行引擎

一、概述 Java程序运行时&#xff0c;JVM会加载.class字节码文件&#xff0c;但是字节码并不能直接运行在操作系统之上&#xff0c;而JVM中的执行引擎就是负责将字节码转化为对应平台的机器码让CPU运行的组件。 执行引擎是JVM核心的组成部分之一。可以把JVM架构分成三部分&am…

route 与 router 之间的差别

简述&#xff1a; router&#xff1a;主要用于处理一些动作&#xff0c; route&#xff1a;主要获得或处理一些数据&#xff0c;比如地址、参数等 例&#xff1a; videoInfo1.vue&#xff1a; <template><div class"video-info"><h3>二级组件…

SamWaf开源轻量级的网站应用防火墙(安装包),私有化部署,加密本地存储的数据,易于启动,并支持 Linux 和 Windows 64 位和 Arm64

一、SamWaf轻量级开源防火墙介绍 &#xff08;文末提供下载&#xff09; SamWaf网站防火墙是一款适用于小公司、工作室和个人网站的开源轻量级网站防火墙&#xff0c;完全私有化部署&#xff0c;数据加密且仅保存本地&#xff0c;一键启动&#xff0c;支持Linux&#xff0c;Wi…

极客说|利用 Azure AI Agent Service 创建自定义 VS Code Chat participant

作者&#xff1a;卢建晖 - 微软高级云技术布道师 「极客说」 是一档专注 AI 时代开发者分享的专栏&#xff0c;我们邀请来自微软以及技术社区专家&#xff0c;带来最前沿的技术干货与实践经验。在这里&#xff0c;您将看到深度教程、最佳实践和创新解决方案。关注「极客说」&a…

windows + visual studio 2019 使用cmake 编译构建静、动态库并调用详解

环境 windows visual studio 2019 visual studio 2019创建cmake工程 1. 静态库.lib 1.1 静态库编译生成 以下是我创建的cmake工程文件结构&#xff0c;只关注高亮文件夹部分 libout 存放编译生成的.lib文件libsrc 存放编译用的源代码和头文件CMakeLists.txt 此次编译CMak…

【kafka实战】05 Kafka消费者消费消息过程源码剖析

1. 概述 Kafka消费者&#xff08;Consumer&#xff09;是Kafka系统中负责从Kafka集群中拉取消息的客户端组件。消费者消费消息的过程涉及多个步骤&#xff0c;包括消费者组的协调、分区分配、消息拉取、消息处理等。本文将深入剖析Kafka消费者消费消息的源码&#xff0c;并结合…