C++ 继承(1)

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++;本质上在编译的时候都是一样的

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

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

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

相关文章

C++入门基础篇:内存管理

本文是C内存管理部分的学习分享 希望能够对你有所帮助~ 那咱们废话不多说&#xff0c;直接开始吧&#xff01; 1. 内存分布 1.1 引入 在开始之前&#xff0c;我们先来看一道题目&#xff1a; int globalVar 1; static int staticGlobalVar 1; void Test() {static int st…

DeepSeek开源多模态大模型Janus-Pro部署

DeepSeek多模态大模型部署 请自行根据电脑配置选择合适环境配置安装conda以及gitJanus 项目以及依赖安装运行cpu运行gpu运行 进入ui界面 请自行根据电脑配置选择合适 本人家用电脑为1060&#xff0c;因此部署的7B模型。配置高的可以考虑更大参数的模型。 环境配置 安装conda…

Chrome谷歌多开教程:实用方法与工具

不管是电子商务、技术测试、空投等不同专业领域&#xff0c;还是个人的工作和生活账号管理&#xff0c;使用不同的独立账户往往需要借助Chrome谷歌浏览器多开来提高效率。Chrome谷歌多开有哪些方法和工具&#xff1f;可以来参考以下实用内容。 一、Chrome谷歌多开方法与工具 1…

Hdoop之MapReduce的原理

简单版本 AppMaster: 整个Job任务的核心协调工具 MapTask: 主要用于Map任务的执行 ReduceTask: 主要用于Reduce任务的执行 一个任务提交Job --> AppMaster(项目经理)--> 根据切片的数量统计出需要多少个MapTask任务 --> 向ResourceManager(Yarn平台的老大)索要资源 --…

Palatir和它的AIP

Palantir是一家成立于2001年的美国大数据分析公司&#xff0c;由彼得Thiel创立&#xff0c;最初专注于反恐数据分析&#xff0c;后来逐步扩展到政府、金融、医疗等多个领域。其核心产品包括Gotham&#xff08;面向政府&#xff09;、Foundry&#xff08;面向商业&#xff09;、…

html 列动态布局

样式说明&#xff1a; /* 列动态布局&#xff0c;列之间以空格填充 */ li {display: flex;/* flex-direction: column; */justify-content: space-between; }

【C++高并发服务器WebServer】-13:多线程服务器开发

本文目录 一、多线程服务器开发二、TCP状态转换三、端口复用 一、多线程服务器开发 服务端代码如下。 #include <stdio.h> #include <arpa/inet.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <pthread.h>s…

活动预告 | 为 AI 新纪元做好准备:助力安全的业务转型

课程介绍 随着现代办公模式的不断演变和 AI 技术的迅速发展&#xff0c;企业在享受效率提升的同时&#xff0c;也面临着信息安全与数据保护的严峻挑战。在利用 AI 技术释放业务潜力的同时&#xff0c;如何确保数据质量与安全已成为企业发展的关键议题。 在本次线上课程中&…

鸿蒙harmony 手势密码

1.效果图 2.设置手势页面代码 /*** 手势密码设置页面*/ Entry Component struct SettingGesturePage {/*** PatternLock组件控制器*/private patternLockController: PatternLockController new PatternLockController()/*** 用来保存提示文本信息*/State message: string …

紧跟潮流,将 DeepSeek 集成到 VSCode

Visual Studio Code&#xff08;简称 VSCode&#xff09;是一款由微软开发的免费开源代码编辑器&#xff0c;自 2015 年发布以来&#xff0c;凭借其轻便、强大、且拥有丰富扩展生态的特点&#xff0c;迅速成为了全球开发者的首选工具。VSCode 支持多平台操作系统&#xff0c;包…

21.2.6 字体和边框

版权声明&#xff1a;本文为博主原创文章&#xff0c;转载请在显著位置标明本文出处以及作者网名&#xff0c;未经作者允许不得用于商业目的。 通过设置Rang.Font对象的几个成员就可以修改字体&#xff0c;设置Range.Borders就可以修改边框样式。 【例 21.6】【项目&#xff…

Windows下AMD显卡在本地运行大语言模型(deepseek-r1)

Windows下AMD显卡在本地运行大语言模型 本人电脑配置第一步先在官网确认自己的 AMD 显卡是否支持 ROCm下载Ollama安装程序模型下载位置更改下载 ROCmLibs先确认自己显卡的gfx型号下载解压 替换替换rocblas.dll替换library文件夹下的所有 重启Ollama下载模型运行效果 本人电脑配…

node.js + html + Sealos容器云 搭建简易多人实时聊天室demo 带源码

node.js html Sealos容器云 搭建简易多人实时聊天室demo 带源码 前言功能介绍&#xff08;demo演示&#xff09;sealos官网配置node.js 编写服务端代码前端ui 调用接口整体项目目录部署到服务器 前言 hello哦盆友们&#xff0c;这次我们来十几行代码做一个超简单的多人聊天…

MYSQL索引与视图

一、新建数据库 mysql> create database mydb15_indexstu; mysql> use mydb15_indexstu; 二、新建表 &#xff08;1&#xff09;学生表Student mysql> create table Student(-> Sno int primary key auto_increment,-> Sname varchar(30) not null unique,-…

win10向windows server服务器传输文件

win10向windows server服务器传输文件 遇到无法直接拖动文件进行传输时 解决方案&#xff1a; 1.点击显示选项 2.点击本地资源-详细信息 3.在窗口中选择你需要共享的磁盘 4.然后远程连接到Windows server服务器 5.登录Windows server服务器后&#xff0c;在此电脑下就能看…

【教程】docker升级镜像

转载请注明出处&#xff1a;小锋学长生活大爆炸[xfxuezhagn.cn] 如果本文帮助到了你&#xff0c;欢迎[点赞、收藏、关注]哦~ 目录 自动升级 手动升级 无论哪种方式&#xff0c;最重要的是一定要通过-v参数做数据的持久化&#xff01; 自动升级 使用watchtower&#xff0c;可…

HTML应用指南:利用GET请求获取全国盒马门店位置信息

随着新零售业态的发展,门店位置信息的获取变得至关重要。作为新零售领域的先锋,盒马鲜生不仅在商业模式创新上持续领先,还积极构建广泛的门店网络,以支持其不断增长的用户群体。本篇文章,我们将继续探究GET请求的实际应用,我们使用Python的requests库通过GET请求,从盒马…

Linux内核数据结构之链表

对于链表的优缺点,我们对比数组可以说出一些,但在随机存储的情况下,我们会选择链表来处理,而我们使用双向链表时,经常会定义成如下形式: struct list_node {TYPE data;struct list_node *prev,*next; }; 相对应的链表结构如下: 对于该数据结构定义,存在一个局限,整个…

ctf网络安全题库 ctf网络安全大赛答案

此题解仅为部分题解&#xff0c;包括&#xff1a; 【RE】&#xff1a;①Reverse_Checkin ②SimplePE ③EzGame 【Web】①f12 ②ezrunner 【Crypto】①MD5 ②password ③看我回旋踢 ④摩丝 【Misc】①爆爆爆爆 ②凯撒大帝的三个秘密 ③你才是职业选手 一、 Re ① Reverse Chec…

250207-MacOS修改Ollama模型下载及运行的路径

在 macOS 上&#xff0c;Ollama 默认将模型存储在 ~/.ollama/models 目录。如果您希望更改模型的存储路径&#xff0c;可以通过设置环境变量 OLLAMA_MODELS 来实现。具体步骤如下&#xff1a; 选择新的模型存储目录&#xff1a;首先&#xff0c;确定您希望存储模型的目标目录路…