C++ —— 继承

什么是继承?

继承是指一种代码可以被复用的机制,在一个类的基础上进行扩展,产生的新类叫做派生类,被继承的类叫基类。(也可称为子类和父类)

继承的写法:

class B : 继承方式 A              (类B以public/private/protected的方式继承类A)

当需要继承多个类时:

 class C : 继承方式 A B              (类C以public/private/protected的方式继承类A、B)

注意:谁在前就先继承谁,如上则是先继承A再继承B

继承究竟做了什么?

继承继承,顾名思义就是继承了父类的成员(准确的说是会继承除去析构、构造以外所有的成员)再加上子类的扩展形成新类。

如下图:(请先忽略构造函数部分)子类student继承了父类people的_name和_age,所以这个子类中也有_name和_age成员变量。

继承的特性

继承方式

(父类XX成员以XX继承后,在子类是XX成员)

类成员/继承方式publicprotectedprivate
父类的public成员子类的public成员子类的protected成员子类的private成员
父类的protected成员子类的protected成员子类的protected成员子类的private成员
父类的private成员在子类中不可见在子类中不可见在子类中不可见

 总结:在继承方式和原本的成员权限中选权限更小的,如在父类中为public以protected继承则继承后的权限为protected(权限大小:public<protected<private)

虽然继承方式有三种,但常用的却只有public。

切片

在继承中,往往可以进行用子类对象直接调用父类函数的情况。像前者这种情况往往发生在相似类型身上,因为相似所以可以转换。而父类有的,子类都有,所以程序执行时就会把子类中与父类相同的部分拿出来去调用函数,这种情况就叫切片。

 

例如:将Student的对象s拿去给People的对象p去调用拷贝构造就是使用了切片的特性

子类构造与析构

虽说父类有的子类都有,理论上说构造自己搞定进行,但规定上在构造子类对象时要先调用父类构造函数,再构造子类部分。(继承是一种复用代码的机制,这里也是复用的父类构造,减少了工作量)

这就是为什么Student的构造函数中的初始化列表要调用  People(string name,int age)  这个构造(这个过程同样会发生切片)

构造要手动调用,那析构也要吗? 

手动调用时结果父类部分析构了两次。实际上在子类析构结束后会自动调用父类析构去释放子类中与父类一致的部分,不需要去手动调用。

隐藏(重定义)

当父类与子类出现同名的成员变量或成员函数时,子类成员将屏蔽对父类成员的直接访问,这时通过子类对象去访问这个同名成员时优先访问子类的。

class People
{
public:
	void printf()
	{
		cout << _name << " " << _age << endl;
	}

	People(string name, int age)
		:_name(name),
		_age(age)
	{}

	People(People& p)
		:_name(p._name),
		_age(p._age)
	{}

	~People()
	{
		cout << "~People()" << endl;
	}

	string _name;
	int _age;
};


class Student : public People 
{
public:
	void printf()
	{
		cout << _name << " " << _age << " "<< _Sno<<endl;
	}

	Student(string name, int age, int sno)
		:People(name, age),
		_Sno(sno)
	{}

	~Student()
	{
		cout << "~Student()" << endl;
	}

	//学号
	int _Sno;
};


int main()
{
	Student s("swi",20,34);
	s.printf();
	return 0;
}

 之所以会出现隐藏这种特性与他的搜索逻辑有关,当我们通过子类对象访问成员时,会先去子类中寻找这个成员,如果没找到再去父类中寻找。


以上所讲都是继承的通用的特性,样例均为单继承

多继承

多继承顾名思义就是继承多个类。

class A
{
public:
	void printf()
	{
		cout << _a << endl;
	}

	int _a = 1;
};

class B
{
public:
	void printf()
	{
		cout << _b << endl;
	}

	int _b = 2;
};


//C同时继承了类A和类B(先继承的A,后继承的B)
class C:public A,B
{
public:
	void printf()
	{
		cout << _c << endl;
	}

	int _c = 3;
};


int main()
{
	C* c = new C;
	return 0;
}

查看内存后我们发现,缺省值为1的_a在缺省值为2的_b 的前面,class C:public A,B的顺序能够决定谁先被继承。

 A,B反过来后:

多继承的问题

 假设他们的成员变量分别为_a,_b,_c,_d,那么他们在内存中的存储会如下图:

此时就会出现两个问题:

  • 二义性:当我访问_a时,到底访问哪一个_a。
  • 数据冗余:出现重复数据,占用不必要空间 

解决方法

在继承方式前加上关键字virtual构成虚继承(注意:想要解决菱形继承的问题,放virtual关键字的类是出现重复部分的若干个子类,被virtual修饰的子类的父类称作虚基类

如下图的菱形问题(菱形问题是这类问题的总称,并不一定是菱形):

不处理时:

 使用virual处理后:

虚基类的成员将会在下方找到一片公共空间去存储,这样一来就解决了二义性和数据冗余。

但是原来两条红线存储的又是什么?

将这四行数字重新组合排序可以得到两个地址,去查找这个地址,发现他分别存储了一个十六进制的28,一个十六进制的18。实际上这个地址是虚基表的地址,而虚基表存储的是地址偏移量,该类的部分的起始地址加上偏移量就能找到虚基类的成员变量_a。

 

多态

对不同的对象会有不同的实现方法,即为多种形态。

多态的条件:

  1. 虚函数的重写(父子类虚函数需要三同,三同指函数名、参数、返回值)
  2. 父类的指针或引用去调用

 但是有三种例外:

  1. 协变(基类与派生类的虚函数返回值不同)
  2. 析构函数的重写(看似不符合函数名相同的条件,实际上编译器对其进行了特殊处理,编译后析构函数的名字统一处理成destructor)
  3. 派生类虚函数重写可以不加virtual(但建议写上)

虚函数

virtual修饰的函数叫做虚函数,虚函数只能是类中非静态的成员函数。

虚函数的重写

子类和父类中的虚函数拥有相同的名字,返回值,参数列表,那么称子类中的虚函数重写了父类的虚函数,或者叫做覆盖。(虚函数重写,重写的是函数体)

//成人
class People
{
public:
	virtual void fun()
	{
		cout << "全票" << endl;
	}
};

//儿童
class Child : public People 
{
public:
	virtual void fun()
	{
		cout <<"半票" << endl;
	}
};

void buyTicket(People& p)
{
	p.fun();
}

int main()
{
	People p;
	Child c;
	buyTicket(p);
	buyTicket(c);
	return 0;
}

多态的原理

为什么会访问到不同的虚函数? 

使用父类的指针或引用去调用(用子类来调用就会产生切片),根据其指针或引用可以找到其对应的虚函数表,进而找到虚函数地址然后去调用不同的虚函数。

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

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

相关文章

MCU功耗测量

功耗测量 一、相关概念二、功耗的需求三、测量仪器仪表测量连接SMU功能SMU性能指标 四、功耗测量注意点板子部分存在功耗MCU方面&#xff0c;可能存在干扰项仪器仪表方面 一、相关概念 静态功耗和动态功耗&#xff1a;动态功耗为运行功耗&#xff0c;功耗测量注重每MHz下的功耗…

智能调度|AIRIOT智能车队管理解决方案

客运、货运、汽车租赁、出租运营等行业对车辆管理、车队管理以及司乘人员的管理方式&#xff0c;逐渐向数字化和智能化转型。传统的依赖人工调度、记录和跟踪的管理模式已经难以满足业务发展需要&#xff0c;存在如下痛点&#xff1a; 实时监控与定位功能弱&#xff1a;无法实时…

实验4 数字频率计

实验目的&#xff1a; 1、使用铆孔U7输出一个脉冲&#xff0c;频率不定。 2、使用铆孔V7测量脉冲频率&#xff0c;并在数码管上显示。 实验内容及步骤&#xff1a; 设计原理 测量频率的方法有很多&#xff0c;按照其工作原理分为无源测量法、比较法、示波器法和计数法等。…

restful请求风格的增删改查-----修改and删除

一、修改&#xff08;和添加类似&#xff09; 前端&#xff1a; <script type"text/javascript">function update(){//创建user对象var user {id:$("#id").val(),username:$("#username").val(),password:$("#password").val…

aweraweg

c语言中的小小白-CSDN博客c语言中的小小白关注算法,c,c语言,贪心算法,链表,mysql,动态规划,后端,线性回归,数据结构,排序算法领域.https://blog.csdn.net/bhbcdxb123?spm1001.2014.3001.5343 给大家分享一句我很喜欢我话&#xff1a; 知不足而奋进&#xff0c;望远山而前行&am…

​「Python绘图」绘制小猪佩奇

python 绘制小猪佩奇 一、预期结果 二、核心代码 import turtle print("开始绘制小猪佩奇") pen turtle.Turtle() pen.pensize(4) #pen.hideturtle()pen.speed(1000)pen.color("#ff9bc0","pink") pen.setheading(-30) pen.pu() pen.goto(-100,…

34. BI - 美国大学生足球队的 GCN 案例

本文为 「茶桁的 AI 秘籍 - BI 篇 第 34 篇」 文章目录 美国大学生足球队 Embedding&#xff08;GCN&#xff09; Hi&#xff0c;你好。我是茶桁。 在上一节课中&#xff0c;因为需要&#xff0c;我们先是回顾了一下 Graph Embedding&#xff0c;然后跟大家讲解了 GCN 以及其算…

代码随想录——双指针/滑动窗口(二)

一.最长连续递增序列 go语言 func max(a,b int) int{if a>b{return a}return b }func findLengthOfLCIS(nums []int) int {n:len(nums)maxlen:0for l:0;l<n;l{r:l1for r<n&&nums[r]>nums[r-1]{r}maxlenmax(r-l,maxlen)}return maxlen }cpp int findLengt…

为什么大模型训练需要GPU,以及适合训练大模型的GPU介绍

文章目录 前言 1、为什么大模型训练需要GPU&#xff0c;而非CPU 2、现在都有哪些合适的GPU适合训练&#xff0c;价格如何 前言 今天偶然看到一篇关于介绍GPU的推文&#xff0c;我们在复现代码以及模型训练过程中&#xff0c;GPU的使用是必不可少的&#xff0c;那么大模型训练需…

软件测试(Web自动化测试)

一.自动化测试简介 1.自动化测试是一种把人工驱动的测试行为转化为机器执行的测试过程。 2.使用自动化测试需要满足的3个条件&#xff1a; &#xff08;1&#xff09;项目需求变动不频繁 &#xff08;2&#xff09;项目进度压力不大&#xff0c;时间不紧迫 &#xff08;3&…

python struct模块 处理字节流

首先看一下&#xff0c;struct 的字节顺序格式。 其次是struct的格式对照表。 下面是案例&#xff1a; 单项数据编解码 >>>struct.pack(i,379978) bJ\xcc\x05\x00 >>>struct.pack(>i,379978) b\x00\x05\xccJ解析&#xff1a; >>>struct.unpa…

5.组合与继承

1.面向对象 在C中&#xff0c;面向对象&#xff08;Object-Oriented&#xff09;是一种程序设计范式&#xff0c;它使用“对象”来设计应用程序和软件。面向对象编程&#xff08;OOP&#xff09;的核心概念包括类&#xff08;Class&#xff09;、对象&#xff08;Object&#x…

23.leetcode---从前序与中序中遍历二叉树(Java版)

题目链接: https://leetcode.cn/problems/construct-binary-tree-from-preorder-and-inorder-traversal/submissions/518810727/ 代码: 测试:

建设数字化工厂系统需要哪些核心技术

随着工业4.0时代的来临&#xff0c;数字化工厂系统已成为制造业转型升级的关键所在。数字化工厂系统通过集成各种先进技术&#xff0c;实现生产过程的智能化、自动化和高效化&#xff0c;进而提升企业的竞争力。那么建设这样一个系统究竟需要哪些核心技术呢&#xff1f; 一、工…

如何创建网址静态码?二维码扫码跳转网址的方法

现在很多的网址链接需要转换成二维码之后来使用&#xff0c;比如印刷包装、宣传单、公众号等方面应用&#xff0c;用户可以通过扫码跳转到对应链接的页面&#xff0c;查看页面内容。那么想要将链接转换二维码&#xff0c;并且二维码长期有效&#xff0c;可以使用生成静态码的方…

CSS文本属性与字体属性

目录 文本属性 文本颜色 文本对齐 修饰文本 文本缩进 行高 字体属性 字体系列 字体大小 字体粗细 字体样式 字体/文本综合属性写法 Chrome调试工具的使用 文本属性 文本颜色 在CSS中使用color 属性用于定义文本的颜色&#xff0c;使用background-color设置一个盒…

attempt to compare nil with number -- 黑马点评出现问题

问题情况 : 主要问题 : 调用lua执行redis时&#xff0c;有一个值会接受nil&#xff08;因为redis中没有该数据&#xff09;或者数值&#xff0c;当该值为nil时执行报错&#xff0c;因为会用到将该值与其他数字比较&#xff0c;故报错attempt to compare nil with number 当然…

Linux 底软开发——对CAN的详细操作(周期发送,异常检测,过滤报文)

Linux底软开发—对CAN发送接收详细操作 文章目录 Linux底软开发—对CAN发送接收详细操作1.保证多条CAN数据发送的周期性2.解析CAN报文数据3.CAN总线异常机制应对4.对CAN报文进行过滤操作5.完整的接收报文代码&#xff08;过滤&#xff0c;心跳检测&#xff0c;解析&#xff09;…

大语言模型Ollama

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl Ollama简介 Ollama是一个开源的大语言模型平台&#xff0c;它允许用户在本地环境中运行、创建和共享大型语言模型。Ollama提供了丰富的功能和特性&#xff0c;使得用户可以…

eCharts 折线图 一段是实线,一段是虚线的实现效果

在lineStyle里写了不生效的话&#xff0c;可以尝试数据拼接 option {xAxis: {type: category,data: [Mon, Tue, Wed, Thu, Fri, Sat, Sun]},yAxis: {type: value},series: [{data: [150, 230, 224,218 ,,,],type: line},{data: [,,, 218, 135, 147, 260],type: line,lineStyl…