C++继承和多态

目录

继承

继承的意义

访问限定符、继承方式

赋值兼容规则(切片)

子类的默认成员函数

多继承

继承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.mzph.cn/bicheng/46119.shtml

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

相关文章

Algorithms,最全的Python算法仓库!

学习编程、学习Python最好的方式就是练习&#xff0c;哪怕是新手&#xff0c;只要不断地敲代码输出&#xff0c;肯定会有神效。 Python的练手项目很多&#xff0c;特别是Github上&#xff0c;建议不管新手、老司机都去看看。 这里推荐给大家一个Gitthub上练习的项目&#xff…

高级Puppet manifest编写和模块化管理:构建高效可靠的自动化运维平台

高级Puppet manifest编写和模块化管理&#xff1a;构建高效可靠的自动化运维平台 Puppet是一种流行的开源自动化运维工具&#xff0c;可以自动化IT基础设施的配置和管理。Puppet使用声明性语言来描述系统状态&#xff0c;并通过客户端-服务器模型来实现自动化。Puppet manifes…

[C++]——同步异步日志系统(5)

同步异步日志系统 一、日志消息格式化设计1.1 格式化子项类的定义和实现1.2 格式化类的定义和实现 二、日志落地类设计2.1 日志落地模块功能实现与测试2.2 日志落地模块功能功能扩展 一、日志消息格式化设计 日志格式化模块的作用&#xff1a;对日志消息进行格式化&#xff0c…

深度学习工具和资源推荐:全面指南

今天我们来聊聊深度学习的工具和资源。要学好深度学习&#xff0c;除了理论知识&#xff0c;还需要掌握一些强大的工具和找到好的资源。以下是我在学习过程中发现的一些非常有用的工具和资源&#xff0c;希望对你们有帮助。 目录 工具推荐 1. Python编程语言 2. TensorFlow…

接口测试返回参数的自动化对比!

引言 在现代软件开发过程中&#xff0c;接口测试是验证系统功能正确性和稳定性的核心环节。接口返回参数的对比不仅是确保接口功能实现的手段&#xff0c;也是测试过程中常见且重要的任务。为了提高对比的效率和准确性&#xff0c;我们可以通过自动化手段实现这一过程。本文将…

WGCLOUD登录页面支持输入验证码吗

支持的 v3.5.3版本开始&#xff0c;WGCLOUD支持在登录页面配置输入验证码&#xff0c;我们可以根据自己的场景需要&#xff0c;配置是否在登录页面显示验证码&#xff0c;如下说明 登录页面添加验证码说明 - WGCLOUD

[超级详细系列]ubuntu22.04配置深度学习环境(显卡驱动+CUDA+cuDNN+Pytorch)--[3]安装cuDNN与Pytorch

本次配置过程的三篇博文分享分别为为&#xff1a; [超级详细系列]ubuntu22.04配置深度学习环境(显卡驱动CUDAcuDNNPytorch)--[1]安装显卡驱动 [超级详细系列]ubuntu22.04配置深度学习环境(显卡驱动CUDAcuDNNPytorch)--[2]安装Anaconda与CUDA [超级详细系列]ubuntu22.04配置深…

代码随想录 day38 动态规划part04

416. 分割等和子集 给定一个只包含正整数的非空数组。是否可以将这个数组分割成两个子集&#xff0c;使得两个子集的元素和相等。注意: 每个数组中的元素不会超过 100 数组的大小不会超过 200示例 1:输入: [1, 5, 11, 5] 输出: true 解释: 数组可以分割成 [1, 5, 5] 和 [11]. …

Web学习day04

mybatis 目录 mybatis 文章目录 一、查询 1.1结果映射 1.2多条件查询 1.3模糊查询 二、XML 书写规范 三、动态SQL 四、配置文件 4.1settings标签 4.2mappers标签 4.3environments标签 五、案例 5.1数据表 5.2实现类 5.3mapper实现 5.4工具类实现 5.5XML动态…

解决spring boot中使用拦截器导致swagger文档无法访问

目录 问题场景 解决方案 问题场景 我们的spring boot项目通常会使用接口文档管理依赖如knife4j(swagger3) Spring Boot3整合knife4j(swagger3)_springboot3 knife4j-CSDN博客 通常也会使用拦截器来做登录鉴权、接口限流等操作&#xff0c;但是使用拦截器会导致swagger接口…

FFmpeg学习(五)-- libswresample使用说明及函数介绍

libswresample Audio合成和重采样 libswresample库用来进行audio数据的合成和重采样操作。调用流程&#xff1a; 调用 swr_alloc 创建SwrContext结构体。设置SwrContext参数&#xff0c;有两种方法&#xff1a; 调用av_opt_set_xx函数逐项设置参数&#xff1b;swr_alloc_set_…

Python应用 | 基于flask-restful+AntDesignVue实现的一套图书管理系统

本文将分享个人自主开发的一套图书管理系统&#xff0c;后端基于Python语言&#xff0c;采用flask-restful开发后端接口&#xff0c;前端采用VueAntDesignVue实现。对其他类似系统的实现&#xff0c;比如学生管理系统等也有一定的参考作用。有问题欢迎留言讨论~ 关注公众号&am…

04.为什么line-height是无单位的 兄弟元素淡出效果 蚀刻文字效果

为什么 CSS 中的 line-height 应该是无单位的? 我经常听说 line-height 应该始终是无单位的。在我早期的编码年代,我没有过多地质疑这一点,但最近我开始想知道为什么会这样。在我看来,1.5 和 1.5em 应该产生相同的结果,对吧?事实证明,它们并非如此。 两者之间有一个细微的区…

记一下blender曲线阵列

先说一下如何正常使用这个 这一次我是用来贴瓷砖 随便创建一个mesh 然后添加一个阵列修改器&#xff0c;然后再给他添加一个curve修改器&#xff0c;使用constant offset去偏移他 这里有个小细节 我第一次创建的curve 我选取之后&#xff0c;死活无法沿着曲线阵列&#xff…

Dockerfile指令

Dockerfile指令 1、Dockerfile指令? 1)、COPY&#xff1a;类似ADD&#xff0c;将我们的文件拷贝到镜像中&#xff0c;也就是复制本地(宿主机)上的文件到镜像。 COPY [--chown<user>:<group>] ["<源路径1>",... "<目标路径>"] …

.快速幂.

按位与&#xff08;Bitwise AND&#xff09;是一种二进制运算&#xff0c;它逐位对两个数的二进制表示进行运算。对于每一位&#xff0c;只有两个相应的位都为1时&#xff0c;结果位才为1&#xff1b;否则&#xff0c;结果位为0。如&#xff1a;十进制9 & 5转化为二进制&am…

ActiveMQ-CVE-2023-46604

Apache ActiveMQ OpenWire 协议反序列化命令执行漏洞 OpenWire协议在ActiveMQ中被用于多语言客户端与服务端通信。在Apache ActvieMQ5.18.2版本以及以前&#xff0c;OpenWire协议通信过程中存在一处反序列化漏洞&#xff0c;该漏洞可以允许具有网络访问权限的远程攻击者通过操作…

opencv 中如何通过欧式距离估算实际距离(厘米)

1&#xff1a;这个方法个人测试觉得是正确的&#xff0c;误差较小&#xff0c;目前满足我当前的需求&#xff0c;如果方法不对&#xff0c;请大家评论&#xff0c;完善。 2&#xff1a;确保拍摄的参照物是垂直的&#xff0c;如果不垂直&#xff0c;就会有误差&#xff0c;不垂…

Django是干什么的?好用么?

Django是一个开源的Python Web框架&#xff0c;用于快速开发高质量的Web应用程序。它提供了许多功能和工具&#xff0c;以简化常见的Web开发任务&#xff0c;如路由、请求处理、数据库管理等。 Django的优点包括&#xff1a; 简单易用&#xff1a;Django提供了清晰的文档和丰…

HASHTABLE, HASHMAP,TreeMap区别

Hashtable、HashMap 和 TreeMap 都是Java集合框架中的实现&#xff0c;它们提供了键值对映射的数据结构&#xff0c;但它们在实现细节、性能特性和使用场景上有所不同&#xff1a; 1. Hashtable: - Hashtable 是遗留下来的类&#xff0c;继承自 Dictionary 类。 - 它实现…