C++之 继承 (inheritance)

news2024/10/2 1:25:56

目录

启示

一、基本语法

二、继承的方式

三种:

公共基础 / 保护继承 / 私有继承

三、继承中的对象模型

①父类中所有非静态成员属性都会继承给子类

②而私有成员属性同样继承过去,但是被编译器隐藏,因此无法访问

四、继承中构造和析构顺序

构造的顺序:      父类 > 子类

析构的顺序:        子类 > 父类

五、继承同名成员处理方式

六、继承同名静态成员处理方式

七、多继承

八、菱形继承


启示

首先,对于一个学校,他有自己的学院/部门,学院下有自己的专业,如图

继承就是承接上一级的内容,同时延续自己下一级的内容


一、基本语法

class 子类 : 继承方式 父类
{}

子类又称派生类 ,体现了个性

父类又称基类,体现了共性

继承方式有public/private/protected


二、继承的方式

三种:

公共基础 / 保护继承 / 私有继承

他们有如下关系

 显而易见的,父类中的private,无论是哪种继承方式,子类都不可访问

接下来我们使用代码进行测试


首先是public公共继承

class B :public A
{
public:
	void func()
	{
		a = 1;// 父类中公共权限成员,到子类中仍然是公共权限成员
		b = 2;// 父类中保护权限成员,到子类中仍然是保护权限成员
		c = 3;// 父类中私有权限成员,不可访问
	}
};

 c不可访问 

 同时,带上测试函数

 因为b是保护权限成员,只能在类内访问,类外不可访问


然后是protected保护继承

class B :protected A // 保护继承
{
public:
	void func()
	{
		a = 1;// 父类中公共权限成员,到子类中变成是保护权限成员
		b = 2;// 父类中保护权限成员,到子类中仍然是保护权限成员
		c = 3;// 父类中私有权限成员,不可访问
	}
};

 c不可访问

接下来是测试函数

可以看到,由于a/b是保护权限成员,因此类外都不能访问 


最后是private私有继承

class B :private A // 私有继承
{
public:
	void func()
	{
		a = 1;// 父类中公共权限成员,到子类中变成是私有权限成员
		b = 2;// 父类中保护权限成员,到子类中仍然是私有权限成员
		c = 3;// 父类中私有权限成员,不可访问
	}

c不可访问 

测试函数:

 同样的,由于a/b是私有权限成员,因此类外都不能访问 


三、继承中的对象模型

①父类中所有非静态成员属性都会继承给子类

②而私有成员属性同样继承过去,但是被编译器隐藏,因此无法访问

class Base // 父类
{
public:
	int a;
protected:
	int b;
private:
	int c;
};
class Son :public Base
{
public:
	int d;
};
void test01()
{
	cout << sizeof(Son) << endl;
}


接下来我们使用vs的开发人员命令提示符工具展示真实的分布情况

①打开 vs2022的开发人员命令提示符工具

② 跳转盘符:输入F:,点击回车

③再接着输入 cd 具体路径, 点击回车

 ④接下来输入 cl d1 reportSingleClassLayout子类名 文件名

文件名可以输入首字符,然后按一下Tab,就会自动补全

 可以看到子类Son父类Base,总Size是16。

子类包含父类的abc变量和自身的d


四、继承中构造和析构顺序

子类继承父类后,创建子类对象,也会调用父类的构造函数

那么构造和析构的顺序是什么?


下面创建父类Base子类Son,并添加各自的构造和析构函数

class Base // 父类
{
public:
	Base()
	{
		cout << "父类构造函数" << endl;
	}
	~Base()
	{
		cout << "父类析构函数" << endl;
	}
};
class Son :public Base // 子类
{
public:
	Son()
	{
		cout << "子类构造函数" << endl;
	}
	~Son()
	{
		cout << "子类析构函数" << endl;
	}
};
void test01()
{
	Son s;
}

 因此,

构造的顺序:      父类 > 子类

析构的顺序:        子类 > 父类


五、继承同名成员处理方式

当父类和子类出现同名对象或函数

①访问子类同名成员,直接访问

②访问父类同名成员,加作用域(所有同名) 


 创建父类Base子类Son,并在2者种加入属性m_A,在测试函数test01中尝试输出m_A

class Base
{
public:
	Base()
	{
		m_A = 50; 
	}
	int m_A;
};
class Son :public Base
{
public:
	Son()
	{
		m_A = 100;
	}
	int m_A;
};

 直接输出,是子类同名成员

若要输出父类同名成员,要加上作用域


 同理,如果要输出同名成员函数,也是要加父类的作用域

class Base
{
public:
	Base()
	{
		m_A = 50; 
	}
	void func()
	{
		cout << "Base_func" << endl;
	}
	int m_A;
};
class Son :public Base
{
public:
	Son()
	{
		m_A = 100;
	}	
	void func()
	{
		cout << "Son_func" << endl;
	}
	int m_A;
};
void test02()
{
	Son s;
	s.func();
	s.Base::func();
}


 同时,如果子类出现了和父类的同名函数,子类将会隐藏掉父类中所有的同名函数,如下

class Base
{
public:
	Base()
	{
		m_A = 50; 
	}
	void func()
	{
		cout << "Base_func" << endl;
	}
	void func(int a) // 同名函数,而且重载
	{
		cout << "Base_fun(int a)" << endl;
	}
	int m_A;
};
class Son :public Base
{
public:
	Son()
	{
		m_A = 100;
	}	
	void func()
	{
		cout << "Son_func" << endl;
	}

	int m_A;
};

子类中有func函数,而父类也有func函数以及其重载函数func(int a

此时即使子类没有同名重载函数,也不能直接调用重载函数,因为被隐藏

需要加父类的作用域 

 


六、继承同名静态成员处理方式

与同名成员相同的处理方式

①访问子类同名成员,直接访问

②访问父类同名成员,加作用域(所有)


父类Base与子类Son,都有静态成员static m_a; 

class Base
{
public:
	static int m_a; // 类内声明
};
int Base::m_a = 10; // 类外初始化
class Son :public Base
{
public:

	static int m_a;
};
int Son::m_a = 5;

接下来使用2种方式访问:①通过对象访问②通过类名访问

void test01()
{
	// 通过对象访问
	Son s;
	cout << s.m_a << endl;
	cout << s.Base::m_a << endl;

	// 通过类名访问
	cout << Son::m_a << endl;
	cout << Base::m_a << endl;
	cout << Son::Base::m_a << endl;
}

 值得注意的是,Son::Base::m_a ,意思是,

Son::通过类名访问Base::作用域下的m_a数据

而Base::m_a是直接通过类名访问m_a


同样的,如果要访问静态成员函数,也是加作用域或不加作用域

class Base
{
public:
	static int m_a;
	static void func()
	{
		cout << "Base的静态成员函数" << endl;
	}
};
int Base::m_a = 10;
class Son :public Base
{
public:

	static int m_a;
	static void func()
	{
		cout << "Son的静态成员函数" << endl;
	}
};
int Son::m_a = 5;
void test02()
{
	// 通过对象访问
	Son s;
	s.func();
	s.Base::func();

	// 通过类名访问
	Son::func();
	Base::func();
	Son::Base::func();
}


 同理,如果父类中出现了同名静态成员函数的重载,也会被隐藏,在调用时必须要加父类的作用域


七、多继承

语法:

class 子类: 继承方式 父类1,继承方式 父类 2......

不建议使用


例:

class Base1 // 父类1
{
public:
	Base1()
	{
		m_a = 100;
	}
	int m_a;
};
class Base2 // 父类2
{
public:
	Base2()
	{
		m_b = 200;
	}
	int m_b;
};
//class 子类: 继承方式 父类1,继承方式 父类 2......
class Son :public Base1, public Base2
{
public:
	Son()
	{
		m_c = 300;
		m_d = 400;
	}
	int m_c;
	int m_d;
};

使用Son类创建对象,并查看其大小

void test01()
{
	Son s;
	cout << sizeof(s) << endl;
}

4个int,大小16

 使用开发人员命令提示符也可以看到包含父类1和父类2的两个int,以及自己的2个int,一共是16


而当父类中出现同名成员时,不可以直接输出 

class Base1 // 父类1
{
public:
	Base1()
	{
		m_a = 100;
	}
	int m_a;
};
class Base2 // 父类2
{
public:
	Base2()
	{
		m_a = 200;
	}
	int m_a;
};
//class 子类: 继承方式 父类1,继承方式 父类 2......
class Son :public Base1, public Base2
{
public:
	Son()
	{
		m_c = 300;
		m_d = 400;
	}
	int m_c;
	int m_d;
};

可以看到,父类1和父类2都有m_a

尝试输出时,会提示不明确,因为重名了

因此,需要加作用域


八、菱形继承

 

 ①传电动汽车继承了汽车的price,纯汽油汽车同样继承了汽车的price,当混合动力汽车使用公共属性price时,就会产生二义性

②混合动力汽车继承自汽车的属性price继承了2份,造成额外开销,只需一份即可

代码:

class car // 父类 车类
{
public:
	int price;
};

class pure_gasoline_car:public car // 纯汽油汽车
{};
class pure_electric_vehicle:public car // 纯电动汽车
{};
class hybrid_electric_vehicle :public pure_gasoline_car, public pure_electric_vehicle //混合动力汽车
{};

而在测试函数中,不可直接调用任何一个的price

void test01()
{
	hybrid_electric_vehicle c;
	c.price = 10;
}

必须要加上作用域 

void test01()
{
	hybrid_electric_vehicle c;
	c.pure_gasoline_car::price = 10;
	c.pure_electric_vehicle::price = 20;

	cout << c.pure_gasoline_car::price << endl;
	cout << c.pure_electric_vehicle::price << endl;
}

打印输出

但是,菱形继承导致数据有2份,造成资源浪费

使用开发人员命令提示符工具查看

 

确实会有2份

接下来,使用虚继承解决 

	//				在public前加上virtua,变为虚继承
class pure_gasoline_car:virtual public car // 纯汽油汽车
{};
class pure_electric_vehicle:virtual public car // 纯电动汽车
{};

在2个子类的继承方式前加上virtual,即变成虚继承

此时,原父类/基类称为虚基类

再次输出

可以发现 打印出了同样的数据,因为此时两个子类的price共用同一块空间

因此我们不再需要区分作用域,也可以直接打印出price

 使用开发人员命令提示符工具查看

 可以看到,pure_gasoline_car的vbptr(虚基类指针)指向它的虚基类表(下面红色),起始位置是0(左边红色),偏移量是8(下面虚基类表里写的8),因此指向8的位置

同理,pure_electric_vehicle的虚基类指针从4偏移4到8的位置,也指向8

因此,两个虚基类指针指向同一块空间

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

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

相关文章

2023年,送你一份最新的后端架构师知识图谱

这是一个能让你成为架构师的文章&#xff0c;请耐心读完。 为什么写这个 前几天心血来潮搜了下《后端架构师》的技术图谱。发现最新最火的文章更新时间还停留在5年前。最新的技术体系并没有罗列在内。而且文章的颗粒度特别细&#xff0c;是从数据结构和常用算法开始的。这是典…

【加餐 2】Tab 标签页管理

【加餐 2】Tab 标签页管理 对于管理系统,经常需要开启多个标签页,但是每次都需要手动去关闭,很麻烦,所以就有了这个功能,可以一键关闭所有标签页,或者关闭除当前标签页外的所有标签页,对于重要的标签页,可以进行固定至前列,方便下次快速打开。 一、实现效果 实现效…

论文浅尝 | 利用知识图谱增强的Transformer进行跨领域方面抽取

笔记整理&#xff1a;沈小力&#xff0c;东南大学硕士&#xff0c;研究方向为知识图谱链接&#xff1a;https://dl.acm.org/doi/pdf/10.1145/3511808.3557275动机情感分析是自然语言处理的基础任务&#xff0c;它包含介绍了细粒度情感分析中的一个常见任务——基于方面的情感分…

【CSS】鼠标移动到元素上方显示 / 移出盒子范围隐藏案例 ( 子绝父相 | 显示隐藏元素对象 | 鼠标经过样式设置 | 半透明遮罩设置 )

文章目录一、鼠标移动到元素上方显示 / 移出盒子范围隐藏案例要点分析1、子绝父相2、显示隐藏元素对象3、鼠标经过样式设置4、半透明遮罩设置二、代码示例一、鼠标移动到元素上方显示 / 移出盒子范围隐藏案例要点分析 1、子绝父相 这里要 在一个 div 盒子上方套一层遮罩 , 遮罩…

【原理图专题】OrCAD Capture 设计规则(DRC)检查

在原理图设计完成后,需要进行DRC检查,DRC检查能协助工程师快速检查原理图的物理、电气规则是否正确,能快速定位错误和原因。 DRC检查从Capture 工具栏中如下图红框所示的图标中Design Rules Check进入 进入后将打开DRC窗口,有四个选项卡。分别是Design Rules Options、Elec…

带你了解攻击与防护相关知识

目录 一、攻击篇 &#xff11;.什么是恶意软件&#xff1f; &#xff12;.恶意软件有哪些特征&#xff1f; 3. 恶意软件的可分为那几类&#xff1f; 4. 恶意软件的免杀技术有哪些&#xff1f; 5. 反病毒技术有哪些&#xff1f; 6. 反病毒网关的工作原理是什么&#xff1f…

java计时器

在 Java中&#xff0c;我们有一个重要的概念&#xff1a;同步和异步。同步就是 Java中的线程安全&#xff0c;异步就是 Java中的线程非安全。 在使用 JVM时&#xff0c;我们一般都是用 start &#xff08;&#xff09;方法启动一个线程&#xff0c;然后设置时间&#xff0c;比如…

Android开发中,自定义注解的两种应用方式

java注解在Android开发中主要有两种使用方式&#xff1b;一种是在程序运行期间获取类的信息进行反射调用&#xff1b;另一种是使用注解处理&#xff0c;在编译期间生成相关代码&#xff0c;然后在运行期间通过调用这些代码来实现相关功能。 我们先了解一下注解的分类和关键字 …

前端实用js dom合集

1. 整个网页变为灰色主题&#xff0c;最外层加css样式&#xff1a;filter:grayscale(1) 黑色主题&#xff1a;filter&#xff1a;invert(1) 2.js剪辑视频片段制作gif动图&#xff1a; 效果&#xff1a;点击开始就开始录制&#xff0c;点击结束右边显示生成的gif动图 生成g…

Python程序异常处理

一、什么是异常 异常就是程序运行时发生错误的信号&#xff0c;在程序由于某些原因出现错误的时候&#xff0c;若程序没有处理它&#xff0c;则会抛出异常&#xff0c;程序也的运行也会随之终止&#xff1b; 程序异常带来的问题&#xff1a; 1.程序终止&#xff0c;无法运行…

浙大数据结构(1)

开始学习数据结构(拖了好久终于开干了) 来自【浙江大学】数据结构&#xff08;合149讲&#xff09;陈越 何钦铭 Be a Fighter and Keep Fighting!!! 数据结构(data structure)定义 是计算机中存储&#xff0c;组织数据的方法。通常情况下&#xff0c;精心选择的数据结构可以带…

Chapter7-吞吐量优先的使用场景

7.1 在 Broker 端进行消息过滤 在 Broker 端进行消息过滤&#xff0c;可以减少无效消息发送到 Consumer &#xff0c;少占用网络带宽从而提高吞吐量。 Broker 端有三种方式进行消息过滤 。 7.1.1 消息的 Tag 和 Key 对一个应用来说&#xff0c;尽可能只用一个 Topic &#xff…

【数据结构学习3】线性表-链表、单链表

目录链式存储结构链表概念头结点的意义单链表的定义和表示单链表的基本操作链式存储结构 链表概念 概念 结点在存储器中的位置是任意的&#xff0c;即逻辑上相邻的数据元素在物理上不一定相邻。线性表的链式表示又称为非顺序映像或链式映像用一组物理位置任意的存储单元来存…

三公经费用泛微全过程数字化管理,使用有记录,付款有依据

公开透明是现代财政制度的重要准则和基本特征。组织要以公开、透明、科学的预算制度确定财政支出&#xff0c;贯穿预算编制、执行、监督全过程。 组织常见的费用管理——“三公”经费&#xff0c;通常指因公出国&#xff08;境&#xff09;费、公务用车购置及运行费、公务接待…

富士康转移3000亿产能,iPhone的印度产能倍增,不再“赏饭吃”

日前消息指今年三月份印度的iPhone产量已经是去年的四倍之多&#xff0c;占比将近7&#xff05;&#xff0c;显示出苹果和富士康都在加速提升印度的iPhone产能&#xff0c;凸显出他们的决心&#xff0c;这对中国制造业将带来深远影响。一、富士康对中国制造影响巨大2021年的数据…

leetcode每日一题:数组篇(1/2)

&#x1f61a;一个不甘平凡的普通人&#xff0c;日更算法学习和打卡&#xff0c;期待您的关注和认可&#xff0c;陪您一起学习打卡&#xff01;&#xff01;&#xff01;&#x1f618;&#x1f618;&#x1f618; &#x1f917;专栏&#xff1a;每日算法学习 &#x1f4ac;个人…

key的作用和原理、列表过滤

一、key的作用与原理 虚拟DOM对象的标识。当状态中的数据发生变化时&#xff0c;vue会根据新数据生成新的虚拟DOM。随后vue进行新虚拟DOM与旧虚拟DOM的差异比较&#xff0c;规则如下&#xff1a;对比规则&#xff1a; 旧虚拟DOM中找到了与新虚拟DOM相同的key 若虚拟DOM中内容没…

Iceberg 数据湖是什么?数据湖能解决什么问题?独立于计算层和存储层之间的表格层?

Iceberg 数据湖是什么&#xff1f;数据湖能解决什么问题&#xff1f;独立于计算层和存储层之间的表格层&#xff1f;0. 导读1. Hive数仓遇到的问题2. 一种开放的表格式3. 自下而上的元数据4. 高性能的查询4.1 分区剪裁4.2 文件过滤4.3 RowGroup过滤参考&#xff1a;https://ice…

kafka-4 生产者和消费者

kafka的生产者和消费者四、 生产者4.1 分区分配策略4.2 副本和消息消费4.2.1 副本&#xff08;AR、ISR、OSR&#xff09;4.2.2 HW与LEO4.2.3 ISR 集合和 HW、LEO的关系五、消费者5.1 分区分配策略5.2 消费者offset的存储四、 生产者 4.1 分区分配策略 &#xff08;1&#xff…

【20】linux进阶——linux的数据流和重定向

大家好&#xff0c;这里是天亮之前ict&#xff0c;本人网络工程大三在读小学生&#xff0c;拥有锐捷的ie和红帽的ce认证。每天更新一个linux进阶的小知识&#xff0c;希望能提高自己的技术的同时&#xff0c;也可以帮助到大家 另外其它专栏请关注&#xff1a; 锐捷数通实验&…