当前位置: 首页 > news >正文

C++ 多态

引例:

#include<iostream>
using namespace std;
class Animal
{
public:void speak(){cout<<"动物在说话"<<endl;}
};
class Cat:public Animal
{
public:void speak(){cout<<"小猫在说话"<<endl;}
};
void DoSpeak(Animal &animal)
{animal.speak();
}
void test01()
{ Cat cat;DoSpeak(cat);
}
int main()
{test01();
}

这段代码会显示动物在说话,但函数中的本意是想显示小猫在说话。

因为

void DoSpeak(Animal &animal)

的地址在编译阶段已经被绑定了,

如果想执行小猫在说话,那么就要使用动态多态的技术,使函数的地址在运行时绑定。

零、什么是多态?

多态是面向对象编程中的一个概念,指同一个方法或操作可以被不同的对象调用,产生不同的结果。也可以理解为同一个接口,不同的实现方式。

在多态的概念中,通过继承,子类可以重写父类的方法,从而实现多态。例如,一个父类有一个某方法,子类可以继承该父类,并重写该方法,从而实现不同的行为。

多态的好处在于,可以增强代码的灵活性和可扩展性,让代码更加面向对象。

一、满足动态多态的条件:

1.有继承关系

2.子类重写父类的虚函数(重写:函数除了函数体,函数头一模一样)

二 、动态多态的使用:

父类的指针或引用 执行子类对象

父类中的被重写函数前面加上virtual,变成虚函数。

例如:void DoSpeak(Animal &animal) 中的 Animal &animal

#include<iostream>
using namespace std;
class Animal
{
public:virtual void speak()//虚函数{cout<<"动物在说话"<<endl;}
};
class Cat:public Animal
{
public:void speak(){cout<<"小猫在说话"<<endl;}
};
void DoSpeak(Animal &animal)
{animal.speak();
}
void test01()
{ Cat cat;DoSpeak(cat);
}
int main()
{test01();
}

三、多态的原理

#include<iostream>
using namespace std;
class Animal
{
public:void speak(){cout<<"动物在说话"<<endl;}
};
class Cat:public Animal
{
public:void speak(){cout<<"小猫在说话"<<endl;}
};
void DoSpeak(Animal &animal)
{animal.speak();
}
void test01()
{ Cat cat;DoSpeak(cat);
}
void test02()
{cout<<"sizeof(Animal) = "<<sizeof(Animal)<<endl;
}
int main()
{//test01();test02();
}

父类中的speak没有变成虚函数前,父类的大小时1字节,也就是一个空类

#include<iostream>
using namespace std;
class Animal
{
public:void speak(){cout<<"动物在说话"<<endl;}
};
class Cat:public Animal
{
public:void speak(){cout<<"小猫在说话"<<endl;}
};
void DoSpeak(Animal &animal)
{animal.speak();
}
void test01()
{ Cat cat;DoSpeak(cat);
}
void test02()
{cout<<"sizeof(Animal) = "<<sizeof(Animal)<<endl;
}
int main()
{//test01();test02();
}

加了virtual变成虚函数后,父类大小是8字节,多了一个指针。

#include<iostream>
using namespace std;
class Animal
{
public:virtual void speak(){cout<<"动物在说话"<<endl;}
};
class Cat:public Animal
{
public:void speak(){cout<<"小猫在说话"<<endl;}
};
void DoSpeak(Animal &animal)
{animal.speak();
}
void test01()
{ Cat cat;DoSpeak(cat);
}
void test02()
{cout<<"sizeof(Animal) = "<<sizeof(Animal)<<endl;
}
int main()
{//test01();test02();
}

有虚函数的类会包含一个虚函数指针vfptr

vfptr会指向一个vftable(虚函数表),vftalble中会存放该虚函数的地址。子类中重写虚函数时会将子类的vftable中的地址覆盖为子类中的虚函数地址。

四、多态案例(计算器)

不用多态的版本:

#include<iostream>
using namespace std;
class Calculator
{
public:int getResult(string oper){if(oper=="+")return m_Nums1+m_Nums2;else if(oper=="-")return m_Nums1-m_Nums2;else if(oper=="*")return m_Nums1*m_Nums2;}int m_Nums1;int m_Nums2;
};void test01()
{ Calculator c;c.m_Nums1=10;c.m_Nums2=10;cout<<c.m_Nums1<<"+"<<c.m_Nums2<<"="<<c.getResult("+")<<endl;cout<<c.m_Nums1<<"-"<<c.m_Nums2<<"="<<c.getResult("-")<<endl;cout<<c.m_Nums1<<"*"<<c.m_Nums2<<"="<<c.getResult("*")<<endl;
}
int main()
{test01();
}

如果想要对计算器的操作方式有拓展,需要修改源码。

真正开发中提倡 “开闭原则”

对拓展进行开放,对修改进行关闭

用多态的版本:

#include<iostream>
using namespace std;
class AbstractCalculator
{
public:virtual int getResult(){return 0;}int m_Nums1;int m_Nums2;
};
class AddCalculator:public AbstractCalculator
{
public:int getResult(){return m_Nums1+m_Nums2;}
};
class SubCalculator:public AbstractCalculator
{
public:int getResult()
{return m_Nums1-m_Nums2;
}
};
class MulCalculator:public AbstractCalculator
{
public:int getResult()
{return m_Nums1*m_Nums2;
}
};
void test01()
{ AbstractCalculator *p=new AddCalculator;p->m_Nums1=100;p->m_Nums2=100;cout<<p->m_Nums1<<"+"<<p->m_Nums2<<"="<<p->getResult()<<endl;delete p;p=new SubCalculator;p->m_Nums1=100;p->m_Nums2=100;cout<<p->m_Nums1<<"*"<<p->m_Nums2<<"="<<p->getResult()<<endl;delete p;p=new MulCalculator;p->m_Nums1=100;p->m_Nums2=100;cout<<p->m_Nums1<<"*"<<p->m_Nums2<<"="<<p->getResult()<<endl;delete p;
}
int main()
{test01();
}

 五、纯虚函数与抽象类

六、多态案例(制作饮品)

#include<iostream>
using namespace std;
class AbstractDrinking
{
public://煮水virtual void Boil()=0;//冲泡virtual void Brew()=0;//倒入杯中virtual void PourInCup()=0;//加入辅料virtual void PutSomething()=0;//制作饮品void makeDrink(){Boil();Brew();PourInCup();PutSomething();}
};
class coffee:public AbstractDrinking
{
public://煮水virtual void Boil(){cout<<"煮农夫山泉"<<endl;}//冲泡virtual void Brew(){cout<<"冲泡咖啡"<<endl;}//倒入杯中virtual void PourInCup(){cout<<"倒入杯中"<<endl;}//加入辅料virtual void PutSomething(){cout<<"加入糖与牛奶"<<endl;}
};
class tea:public AbstractDrinking
{
public://煮水virtual void Boil()
{cout<<"煮矿泉水"<<endl;
}//冲泡virtual void Brew()
{cout<<"冲茶叶"<<endl;
}//倒入杯中virtual void PourInCup()
{cout<<"倒入杯中"<<endl;
}//加入辅料virtual void PutSomething()
{cout<<"加入枸杞"<<endl;
}
};
void doWork(AbstractDrinking *abs)
{abs->makeDrink();delete abs;
}
void test01()
{doWork(new coffee);cout<<"------------------"<<endl;doWork(new tea);
}
int main()
{test01();
}

七、虚析构与纯虚析构

父类指针在析构时候,不会调用子类中的析构函数,导致子类如果右堆区属性,出现内存泄露

#include<iostream>
using namespace std;
class Animal
{
public:Animal(){cout<<"Animal的构造函数调用"<<endl;}virtual void speak()=0;~Animal(){cout<<"Animal的析构函数调用"<<endl;}
};
class Cat:public Animal
{
public:Cat(string name){cout<<"Cat的构造函数调用"<<endl;m_Name=new string(name);}void speak(){cout<<*m_Name<<"小猫在说话"<<endl;}~Cat(){if(m_Name!=nullptr){cout<<"Cat的析构函数调用"<<endl;delete m_Name;m_Name=nullptr;}}string *m_Name;
};
void test01()
{Animal *animal=new Cat("tom");animal->speak();delete animal;
}
int main()
{test01();
}

在父类的析构函数前加上virtual,就可以解决问题。

纯虚析构:virtual ~Animal()=0;

类外

Animal::~Animal()
{
    cout<<"Animal纯虚析构函数调用"<<endl;
}

纯虚析构必须要有具体的函数实现

#include<iostream>
using namespace std;
class Animal
{
public:Animal(){cout<<"Animal的构造函数调用"<<endl;}virtual void speak()=0;/*virtual~Animal(){cout<<"Animal的虚析构函数调用"<<endl;}*/virtual~Animal()=0;
};
Animal::~Animal()
{cout<<"Animal纯虚析构函数调用"<<endl;
}
class Cat:public Animal
{
public:Cat(string name){cout<<"Cat的构造函数调用"<<endl;m_Name=new string(name);}void speak(){cout<<*m_Name<<"小猫在说话"<<endl;}~Cat(){if(m_Name!=nullptr){cout<<"Cat的析构函数调用"<<endl;delete m_Name;m_Name=nullptr;}}string *m_Name;
};
void test01()
{Animal *animal=new Cat("tom");animal->speak();delete animal;
}
int main()
{test01();
}

相关文章:

C++ 多态

引例&#xff1a; #include<iostream> using namespace std; class Animal { public:void speak(){cout<<"动物在说话"<<endl;} }; class Cat:public Animal { public:void speak(){cout<<"小猫在说话"<<endl;} }; void Do…...

LeetCode 之 二分查找

网址&#xff1a; LeetCode 704.二分查找 算法模拟&#xff1a; Algorithm Visualizer 在线工具&#xff1a; C 在线工具 如果习惯性使用Visual Studio Code进行编译运行&#xff0c;需要C11特性的支持&#xff0c;可参考博客&#xff1a; VisualStudio Code 支持C11插件配…...

【性能测试】中间件优化

1、Tomcat 优化连接数、线程池 打开tomcat安装目录\conf\server.xml文件&#xff0c;在server.xml中 有以下配置&#xff1a; tomcat HTTP/1.1 <Connector port"8080" protocol"HTTP/1.1" maxThreads"1000" acceptCount"1500" c…...

【算法】查找类——二分查找算法

二分查找算法算法总结 算法描述 该算法属于查找算法。当需要从有序数组中查找某一元素时&#xff0c;可以使用该算法进行查找。&#xff08;本文章假设数组是升序排列的数组&#xff09; 算法思想 每次进行对半查找&#xff0c;获取中间元素值&#xff0c;然后与目标值进行…...

Ansible FIle模块,使用Ansible File模块进行文件管理

当使用 Ansible 进行自动化配置和管理时&#xff0c;file 模块是一个强大的工具&#xff0c;用于在目标主机上创建、修改和删除文件和目录。它提供了一种简单而灵活的方式来处理文件系统操作。在本文中&#xff0c;我们将详细介绍如何使用 Ansible 的 file 模块。 1. 创建文件 …...

索尼mp4变成rsv修复案例(ILME-FX3)

索尼mp4的修复案例讲过很多&#xff0c;这次是索尼的ILME-FX3也算是一个畅销的机型&#xff0c;一般索尼没有封装的文件是RSV文件&#xff0c;但是极少遇到有多个RSV文件的&#xff0c;下边我们来讲下这个特殊案例。 故障文件:4个RSV文件&#xff0c;大小在1.78G~28G多 故障现…...

抓拍摄像机开关量控制4K高清手机远程看图建筑生长定时缩时相机

作为物联网数据采集解决方案专业提供商,数采物联网小编daq-iot 在这里做以下内容介绍,并诚挚的欢迎大家讨论和交流。 项目案例参考视频&#xff1a; https://www.bilibili.com/video/BV1Kp4y1T7wQ/?spm_id_from333.999.0.0 4K高清太阳能供电定时拍照相机&#xff0c;通过光…...

c++使用http请求-drogon框架

创建drogon框架 drogon_ctl create project test_ctrl添加一个控制器 进入controllers目录下 drogon_ctl create controller -h check_ctrl编写主函数 #include <drogon/drogon.h> int main() {//Set HTTP listener address and port//drogon::app().addListener("…...

幼儿棒球运动宣传介绍·野球6号位

幼儿棒球运动宣传介绍 1. 棒球对幼儿成长的重要性 棒球运动对幼儿协调能力和团队协作的培养 棒球运动对幼儿协调能力和团队协作的培养非常重要。通过棒球运动&#xff0c;孩子们可以学习如何与队友合作&#xff0c;如何在压力下保持冷静&#xff0c;以及如何快速做出决策。这…...

grpc多语言通信之GO和DART

都是一个吗生的,找下例子 上一篇文章说到go实现的grpc方法已经实现了一个grpc的server端, 注意: 这两个项目的.proto文件应当是完全一致的,只是方法用各自的语言实现罢了 报错了: Caught error: gRPC Error (code: 12, codeName: UNIMPLEMENTED, message: grpc: Decompresso…...

基于FPGA的RGB图像转Ycbcr实现,包括tb测试文件以及MATLAB辅助验证

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 5.算法完整程序工程 1.算法运行效果图预览 将FPGA的数据导入到matlab进行显示 2.算法运行软件版本 Vivado2019.2 matlab2022a 3.部分核心程序 timescale 1ns / 1ps // // Company: // E…...

centos 编译安装的php多版本 切换

centos 编译安装的php多版本 切换 wheris php php: /usr/bin/php /usr/lib64/php /etc/php.ini /etc/php.d /usr/local/php /usr/local/php7.4 /usr/share/php /usr/share/man/man1/php.1.gz/usr/bin/php: php可执行脚本&#xff0c;任何版本的php 通过软连接到这可以实现全局…...

Unity 性能优化之Shader分析处理函数ShaderUtil.HasProceduralInstancing: 深入解析与实用案例

Unity 性能优化之Shader分析处理函数ShaderUtil.HasProceduralInstancing: 深入解析与实用案例 点击封面跳转到Unity国际版下载页面 简介 在Unity中&#xff0c;性能优化是游戏开发过程中非常重要的一环。其中&#xff0c;Shader的优化对于游戏的性能提升起着至关重要的作用。…...

2023数学建模国赛E题黄河水沙监测数据分析完整代码分析+处理结果+思路文档

已经写出国赛E题黄河水沙监测数据分析完整代码分析处理结果思路分析&#xff08;30页&#xff09;&#xff0c;包括数据预处理、数据可视化&#xff08;分组数据分布图可视化、相关系数热力图可视化、散点图可视化&#xff09;、回归模型&#xff08;决策树回归模型、随机森林回…...

玩转Mysql系列 - 第19篇:游标详解

这是Mysql系列第19篇。 环境&#xff1a;mysql5.7.25&#xff0c;cmd命令中进行演示。 代码中被[]包含的表示可选&#xff0c;|符号分开的表示可选其一。 需求背景 当我们需要对一个select的查询结果进行遍历处理的时候&#xff0c;如何实现呢&#xff1f; 此时我们需要使…...

【量化分析】Python 布林线( Bollinger)概念

一、说明 布林线(BOLL)&#xff0c;Bollinger Bands&#xff0c;利用统计原理&#xff0c;求出股价的标准差及其信赖区间&#xff0c;从而确定股价的波动范围及未来走势&#xff0c;利用波带显示股价的安全高低价位&#xff0c;因而也被称为布林带。 二、布林带基本概念 布林线…...

MySQL的权限管理与远程访问

MySQL的权限管理 1、授予权限 授权命令&#xff1a; grant 权限1,权限2,…权限n on 数据库名称.表名称 to 用户名用户地址 identified by ‘连接口令’; 该权限如果发现没有该用户&#xff0c;则会直接新建一个用户。 比如 grant select,insert,delete,drop on atguigudb.…...

在Qt创建的UI中放一个显示点云的窗口(PCL+QT5)

1、首先在Qt Designer创建UI后&#xff0c;拖一个Widget窗口出来 2、在对象查看器中右击该Widget&#xff0c;选择提升窗口部件&#xff0c;如下操作&#xff1a; 3、把UI转出来放在VS项目中&#xff0c;其中你的UI代码头文件会自带QVTKOpenGLNativeWidget.h&#xff0c;当然你…...

element ui el-table分页多选功能

selection-change&#xff1a;当选择项发生变化时会触发该事件&#xff08;当分页切换时&#xff0c;选中的数据都会自动清空&#xff09; 一、在el-table中添加 :row-key“id” <el-table :data"tableData" border style"width: 95%" selection-change…...

理解网络通信的基础:OSI七层模型与TCP/IP五层模型

在今天的数字化世界中&#xff0c;网络通信已经成为我们日常生活和商业活动的重要组成部分。为了更好地理解和管理网络通信&#xff0c;网络工程师和管理员使用不同的模型来组织和解释网络协议和通信过程。本文将介绍两种最重要的网络模型&#xff1a;OSI七层模型和TCP/IP五层模…...

Python爬虫-爬取文档内容,如何去掉文档中的表格,并保存正文内容

前言 本文是该专栏的第58篇,后面会持续分享python爬虫干货知识,记得关注。 做过爬虫项目的同学,可能或多或少爬取过文档数据,比如说“政务网站,新闻网站,小说网站”等平台的文档数据。爬取文档数据,笔者这里就不过多详述,而本文,笔者将主要介绍在爬取文档数据的过程中…...

【使用Cpolar和Qchan搭建自己的个人图床】

文章目录 前言1. Qchan网站搭建1.1 Qchan下载和安装1.2 Qchan网页测试1.3 cpolar的安装和注册 2. 本地网页发布2.1 Cpolar云端设置2.2 Cpolar本地设置 3. 公网访问测试总结 前言 图床作为云存储的一项重要应用场景&#xff0c;在大量开发人员的努力下&#xff0c;已经开发出大…...

flutter解决多个类名重名问题

Try using ‘as prefix’ for one of the import directives, or hiding the name from all but one of the imports. Flutter遇到这种错误,意思是你自己的import的库的类名跟一另一个导入的库,或者系统的类名名字相同.解决方法,把自己的一个类名用as 加一个前缀,使用的时候 用…...

微信小程序 按钮颜色

<button type"primary">主要按钮样式类型</button> <button type"default">默认按钮样式类型</button> <button type"warn">警告按钮样式类型</button> <view>按钮plain是否镂空</view> <bu…...

【云原生】kubectl常用命令大全

目录 一、资源管理方法 kubectl 的命令大全 二、 kubectl常用命令大全 2.2 项目的生命周期&#xff1a;创建-->发布-->更新-->回滚-->删除 1、创建 kubectl create命令 2、发布 kubectl expose命令 3、更新 kubectl set 4、回滚 kubectl rollou…...

git pull

目录 git pull 原理&#xff1a; git pull遇到问题怎么解决&#xff1a; git pull 原理&#xff1a; git pull 是 Git 版本控制系统中的一个命令&#xff0c;用于从远程存储库更新本地工作目录。它实质上是两个命令的组合&#xff1a;git fetch 和 git merge。 当你执行 gi…...

C++学习之运算符与表达式

算数运算符 基本的算数运算有加法、减法、乘法、除法和取模&#xff08;求余数&#xff09;&#xff0c;对应的算数运算符分别为&#xff1a;、-、*、/、%。至于用法&#xff0c;大家应该耳熟能详&#xff0c;这里不再过多赘述。 自增与自减运算符 运算符说明自增运算符&…...

vue使用谷歌地图实现地点查询

效果 代码 首先在index.html中引入谷歌地图资源 <script src"https://maps.googleapis.com/maps/api/js?key你的api密钥&librariesplaces"></script>页面中 <template><div class"pac-card div-style" id"pac-card"…...

前端该了解的网络知识

网络 前端开发需要了解的网络知识 URL URL(uniform resource locator,统一资源定位符)用于定位网络服务. URL是一个固定格式的字符串 它表达了: 从网络中哪台计算机(domain)中的哪个服务(port),获取服务器上资源的路径(path),以及要用什么样的协议通信(schema). 注意: 当…...

python3在虚拟环境实用vscode调试错误输出ModuleNotFoundError: No module named ‘django‘解决方法

Exception has occurred: ImportError Couldnt import Django. Are you sure its installed and available on your PYTHONPATH environment variable? Did you forget to activate a virtual environment?File "/data/mountain-backend/src/manage.py", line 8, i…...