C++继承和多态

news2024/11/15 12:44:17

目录

继承

继承的意义

访问限定符、继承方式

赋值兼容规则(切片)

子类的默认成员函数

多继承

继承is a和组合has a

多态

什么是多态

形成多态的条件

函数重载,隐藏,重写的区别

override和final

多态原理


继承

继承的意义

继承本质就是类之间代码的复用,通过子类继承父类的方式,让子类对象中具有父类对象的成员。

访问限定符、继承方式

通过访问限定符,可以限制子类可以继承哪些变量,不能继承哪些变量,在实际开发过程中通常使用公有public继承。父类中想要让子类继承的成员就用protected或public修饰,不想让子类看到就用private修饰。

赋值兼容规则(切片)

子类对象,指针,引用分别赋值给父类对象,指针,引用时,会遵循赋值兼容规则(切片)。即把子类对象中父类对象的可见范围给到父类对象、指针、引用

子类的默认成员函数

默认成员函数主要包括构造函数,拷贝构造,赋值运算符重载,析构函数。那么在子类中他们是如何调用的呢?(考虑到要初始化子类对象中的父类对象

子类的初始化包括对子类成员的初始化和对继承的父类成员的初始化

由于父类的默认成员函数在子类内是可见的,所以在初始化父类成员时,只需要调用父类的默认成员函数即可。子类成员再另当初始化。

例如,基类为Person,子类为Student的代码:

class Person
{
public:
	Person(string name,int age,string gender)
		:_name(name),_age(age),_gender(gender)
	{
		cout << "Person()" << endl;
	}
	Person(const Person& p)
	{
		cout << "Person(const Person& p)" << endl;
	}
	Person& operator=(const Person& p)
	{
		cout << "Person.operator=()" << endl;
		return *this;
	}
	~Person()
	{
		cout << "~Person()" << endl;
	}
protected:
	string _name;//姓名
	int _age;//年龄
	string _gender;//性别
};
class Student:public Person
{
private:
	string _id;//学号
	string _college;//所属学院
public:
	Student(string name, int age, string gender,string id,string college)
		:Person(name,age,gender)//调用父类构造
		,_id(id),_college(college)
	{
		//初始化子类成员
		cout << "Student()" << endl;
	}
	Student(const Student& s)
		:Person(s)//调用父类拷贝构造
	{
		//拷贝子类成员
		cout << "Student(const Student& s)" << endl;
	}
	Student& operator=(const Student& s)
	{
		Person::operator=(s);//调用父类的operator=
		cout << "Student.operator=()" << endl;
		//赋值子类成员
		return *this;
	}
	~Student()
	{
		//默认自动先调用子类析构,再调用父类析构
		cout << "~Student()" << endl;
	}
};
int main()
{
	Student s("cx", 20, "男", "2022112102601", "计算机与信息工程学院");//构造
	Student s2 = s;//拷贝构造
	Student s3("xxx", 18, "女", "2022112102728", "经济管理学院");//构造
	s3 = s2;//赋值
	return 0;
}

代码输出结果:

多继承

多继承:多继承就是一个类可以继承多个父类。

多继承会导致的问题:菱形继承,图示如下

为什么说菱形继承会有问题?

由于继承的本质是代码的复用,而菱形继承会导致A类对象在D类对象中存在两份,导致数据冗余和二义性。

如何解决:虚继承

虚继承就是多个类 在继承重复类时,都使用虚继承。这样在上图中D对象内就只会有一份A对象,消除了对象的冗余。

继承is a和组合has a

继承和组合本质都是代码的复用,使得一个对象存在于另一个对象之中。

继承:

class B
{
protected:
	int _b;
};
class A :public B
{
//A中可以直接使用父类的protected和public成员
protected:
	int _a;
};

组合:

class B
{
protected:
	int _b;
};
class A
{
protected:
	B _b;//B类对象作为成员,只能使用B类对象的public成员
	int _a;
};

由于组合只能使用另一个对象的公有成员,这使得组合而成的类之间耦合度更低,因此在组合和继承都可以达到目的时,组合方案更优。

多态

什么是多态

多态:多态就是一个接口多种实现,多种状态,是面向对象编程语言的一重要特征。当使用父类指针或引用接收子类对象指针或引用,并调用子类对象中重写的虚函数时,就会体现出多态性。即指向父类调父类,指向子类调子类

class Animal //基类
{
public:
	virtual void say()//虚函数
	{
		cout << "animal say " << endl;
	}
};
class Cat:public Animal  //子类,重写基类的虚函数
{
public:
	virtual void say()
	{
		cout << "cat say miao~miao~" << endl;
	}
};
class Dog :public Animal  //子类,重写基类的虚函数
{
public:
	virtual void say()
	{
		cout << "dog say wang~wang~" << endl;
	}
};
void func(Animal* n)//基类指针/引用接收
{
	n->say();//多态调用
}
int main()
{
	Animal a;
	Cat c ;
	Dog d;
	func(&a);
	func(&c);
	func(&d);
	return 0;
}

上面的代码基类为Animal,子类有Cat和Dog,当使用基类指针或引用接收对象的指针或引用时,指向哪个类调用哪个类的虚函数。

形成多态的条件

1.具有继承关系

2.虚函数重写

3.父类指针或引用调用虚函数

函数重载,隐藏,重写的区别

重载:同一作用域内,函数名相同,参数不同。

隐藏(重定义):子类函数和父类函数名字相同。父类函数就被隐藏了。

重写(覆盖):子类函数和父类函数名字相同,参数相同,返回类型相同,且父类函数是虚函数。

override和final

子类重写父类虚函数时,用override修饰子类重写的函数,用于检查是否成功重写(三同或者协变)。如果不构成重写在编译时就会报错。

用final修饰的类,不允许有子类继承

用final修饰的虚成员函数,在子类中不可被重写

多态原理

首先看一下这段代码

class A
{
public:
	void func()
	{
		;
	}
};
int main()
{
    A a;
	cout << sizeof(a) << endl;
	return 0;
}

因为类的成员函数存放在代码段,并不会在每个对象中单独存放一份,所以这里是空类,但是打印的是1,是因为要用至少一个字节来标识不同的对象。

class A
{
public:
	virtual void func()
	{
		;
	}
};
int main()
{
	A a;
	cout << sizeof(a) << endl;
	return 0;
}

这个类的大小是多少呢?

这又是为什么呢?为什么函数由普通函数变为虚函数,对象的大小就变了呢?

这是因为类中的每个虚函数地址要放到一个专门的位置,即虚函数表中。

对象中会多存放一个虚函数表指针,虚函数表中存放着一个类中所有的虚函数指针。

指针的大小在32位计算机上为4字节,64位计算机为8字节,所以类的大小变为8字节。

这和多态有什么关系呢?

子类继承父类时,会把父类对象中的虚函数表也继承下来。


重写虚函数前:

子类虚函数表中的虚函数地址和父类虚函数表中的虚函数地址是相同的。

重写之后:子类虚函数表中的虚函数地址会被重写的虚函数地址"覆盖"。

虚函数指针被覆盖后,此后在多态的条件下,基类指针指向父类对象时,编译器会在父类对象的虚函数表中查找虚函数,指向子类对象时,编译器会在子类对象的虚函数表中查找虚函数。进而体现出了指向谁,调用谁的多态性。

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

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

相关文章

FinalShell介绍,安装与应用

目录 一、什么是finalshell 二、finalshell功能 三、为什么要用finalshell 四、安装finalshell 五、finalshell使用 1.添加连接 获取虚拟ip地址 2.启动连接 一、什么是finalshell FinalShell是一体化的的服务器,网络管理软件,不仅是ssh客户端,还是功能强大的开发,运维工…

在RHEL9.4上启用SFTP服务

FTP存在的不足&#xff1a; 明文传输 FTP传输的数据&#xff08;包括用户名、密码和文件内容&#xff09;都是明文的&#xff0c;这意味着数据可以被网络上的任何人截获并读取。没有内置的加密机制&#xff0c;容易受到中间人攻击。 被动模式下的端口问题 FTP的被动模式需要…

server nat表和会话表的作用及NAT地址转换详细

本章节主要讲nat技术的基础 -会话表的建立也是看5元组 -状态检测技术的回包一样也看5元组&#xff0c;但是状态检测技术会看的除开5元组还有更多东西 老哥&#xff0c;你真的应该好好注意一个东西&#xff1a;我们的会话表只是为了后续包的转发&#xff0c;会话表是记录的首…

C++:哈希表

哈希表概念 哈希表可以简单理解为&#xff1a;把数据转化为数组的下标&#xff0c;然后用数组的下标对应的值来表示这个数据。如果我们想要搜索这个数据&#xff0c;直接计算出这个数据的下标&#xff0c;然后就可以直接访问数组对应的位置&#xff0c;所以可以用O(1)的复杂度…

澳门建筑插画:成都亚恒丰创教育科技有限公司

澳门建筑插画&#xff1a;绘就东方之珠的斑斓画卷 在浩瀚的中华大地上&#xff0c;澳门以其独特的地理位置和丰富的历史文化&#xff0c;如同一颗璀璨的明珠镶嵌在南国海疆。这座城市&#xff0c;不仅是东西方文化交融的典范&#xff0c;更是建筑艺术的宝库。当画笔轻触纸面&a…

能源园区可视化管理系统

利用图扑 HT 可视化打造能源园区管理系统&#xff0c;实时监控和优化能源分配&#xff0c;提升园区运行效率&#xff0c;增强安全管理&#xff0c;推动绿色和可持续发展。

信立方大模型 | 以AI之钥,开拓智能守护新疆界

在当前网络安全形势日益复杂的背景下&#xff0c;技术的进步不仅带来了便利&#xff0c;也使得网络攻击手段更加多样化和隐蔽化。据悉&#xff0c;国外某研究团队已成功利用GPT技术开发出一种黑客智能体框架&#xff0c;该框架能够深入研读CVE&#xff08;通用漏洞披露&#xf…

MATLAB激光通信和-积消息传递算法(Python图形模型算法)模拟调制

&#x1f3af;要点 &#x1f3af;概率论和图论数学形式和图结构 | &#x1f3af;数学形式、图结构和代码验证贝叶斯分类器算法&#xff1a;&#x1f58a;多类型&#xff1a;朴素贝叶斯&#xff0c;求和朴素贝叶斯、高斯朴素贝叶斯、树增强贝叶斯、贝叶斯网络增强贝叶斯和半朴素…

Android12 MultiMedia框架之GenericSource extractor

前面两节学习到了各种Source的创建和extractor service的启动&#xff0c;本节将以本地播放为例记录下GenericSource是如何创建一个extractor的。extractor是在PrepareAsync()方法中被创建出来的&#xff0c;为了不过多赘述&#xff0c;我们直接从GenericSource的onPrepareAsyn…

LeetCode刷题笔记第3011题:判断一个数组是否可以变为有序

LeetCode刷题笔记第3011题&#xff1a;判断一个数组是否可以变为有序 题目&#xff1a; 想法&#xff1a; 使用冒泡排序进行排序&#xff0c;在判断大小条件时加入判断二进制下数位为1的数目是否相同&#xff0c;相同则可以进行互换。最后遍历数组&#xff0c;相邻两两之间是…

17集 如何用ESP-IDF编译ESP-DL深度学习工程-《MCU嵌入式AI开发笔记》

17集 如何用ESP-IDF编译ESP-DL深度学习工程-《MCU嵌入式AI开发笔记》 参考文档&#xff1a;ESP-DL 用户指南&#xff1a; https://docs.espressif.com/projects/esp-dl/zh_CN/latest/esp32/index.html 和https://docs.espressif.com/projects/esp-dl/zh_CN/latest/esp32/get-s…

Qt Mqtt客户端 + Emqx

环境 Qt 5.14.2 qtmqtt mqttx 功能 QT Mqtt客户端 qtmqtt 下载 qtmqtt (注意下载与QT版本相符的库)并使用QT 编译 编译完成后需要的文件: emqx 1.虚拟机中安装emqx,并启动 curl -s https://assets.emqx.com/scripts/install-emqx-deb.sh | sudo bash sudo apt-get inst…

Java实现数据结构——双链表

目录 一、前言 二、实现 2.1 类的创建 三、对链表操作实现 3.1 打印链表 3.2 插入数据 3.2.1 申请新节点 3.2.2 头插 ​编辑 3.2.3 尾插 3.2.4 链表长度 3.2.5 任意位置插入 3.3 删除数据 3.3.1 头删 3.3.2 尾删 3.3.3 删除指定位置数据 3.3.4 删除指定数据 3…

王道计算机考研数据结构思维导图笔记(持续更新)

第1章 绪论 1.1 数据结构的基本概念 1.1.1 基本概念和术语 1.1.1 数据结构三要素 1.2 算法和算法评价 1.2.1算法的基本概念 1.2.2 算法效率的度量 第2章 线性表 2.1 线性表的定义和基本操作 2.1.1 线性表的定义 2.1.2 线性表的基本操作 2.2.1 顺序表上的定义 2.2.2 顺序…

Power Apps使用oData访问表数据并赋值前端

在使用OData查询语法通过Xrm.WebApi.retrieveMultipleRecords方法过滤数据时&#xff0c;你可以指定一个OData $filter 参数来限制返回的记录集。 以下是一个使用Xrm.WebApi.retrieveMultipleRecords方法成功的例子&#xff0c;它使用了OData $filter 参数来查询实体的记录&am…

期货交易记录20240714

文章目录 期货交易系统构建步骤一、选品二、心态历练三、何时开仓3.1、开仓纪律3.2、开仓时机3.3、开仓小技巧 四、持仓纪律五、接下来的计划 2024年7月15号&#xff0c;期货交易第6篇记录。这一篇文中主要记录下&#xff0c;根据交易保证金筛选品种。 交易记录&#xff1a;目…

internet download manager(IDM下载器) 6.42.8.2下载安装使用指南

internet download manager(IDM下载器) 6.42.8.2Z是一款功能强大的下载加速工具&#xff0c;能够显著提升您的下载速度&#xff0c;最高可达500%。它不仅能够加速下载&#xff0c;还能对下载任务进行智能调度&#xff0c;并具备恢复中断下载的能力。根据用户评价&#xff0c;无…

6.S081的Lab学习——Lab10: mmap

文章目录 前言mmap(hard)提示&#xff1a;解析 总结 前言 一个本硕双非的小菜鸡&#xff0c;备战24年秋招。打算尝试6.S081&#xff0c;将它的Lab逐一实现&#xff0c;并记录期间心酸历程。 代码下载 官方网站&#xff1a;6.S081官方网站 安装方式&#xff1a; 通过 APT 安装…

Re:从零开始的C++世界——(一)入门基础

文章目录 C发展历史1.命名空间1.1 namespace的价值1.2 namespace的定义1.3 命名空间使⽤ 2.C输⼊&输出3.缺省参数3.1 缺省参数的概念3.2 缺省参数的分类 4.函数重载5.引⽤5.1引⽤的概念和定义5.2 引⽤的特性5.3 const引⽤5.4 使用场景5.5 指针和引⽤的关系 6.内联函数6.1内…

RDNet实战:使用RDNet实现图像分类任务(二)

文章目录 训练部分导入项目使用的库设置随机因子设置全局参数图像预处理与增强读取数据设置Loss设置模型设置优化器和学习率调整策略设置混合精度&#xff0c;DP多卡&#xff0c;EMA定义训练和验证函数训练函数验证函数调用训练和验证方法 运行以及结果查看测试完整的代码 在上…