当前位置: 首页 > 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五层模…...

突破不可导策略的训练难题:零阶优化与强化学习的深度嵌合

强化学习&#xff08;Reinforcement Learning, RL&#xff09;是工业领域智能控制的重要方法。它的基本原理是将最优控制问题建模为马尔可夫决策过程&#xff0c;然后使用强化学习的Actor-Critic机制&#xff08;中文译作“知行互动”机制&#xff09;&#xff0c;逐步迭代求解…...

在HarmonyOS ArkTS ArkUI-X 5.0及以上版本中,手势开发全攻略:

在 HarmonyOS 应用开发中&#xff0c;手势交互是连接用户与设备的核心纽带。ArkTS 框架提供了丰富的手势处理能力&#xff0c;既支持点击、长按、拖拽等基础单一手势的精细控制&#xff0c;也能通过多种绑定策略解决父子组件的手势竞争问题。本文将结合官方开发文档&#xff0c…...

前端开发面试题总结-JavaScript篇(一)

文章目录 JavaScript高频问答一、作用域与闭包1.什么是闭包&#xff08;Closure&#xff09;&#xff1f;闭包有什么应用场景和潜在问题&#xff1f;2.解释 JavaScript 的作用域链&#xff08;Scope Chain&#xff09; 二、原型与继承3.原型链是什么&#xff1f;如何实现继承&a…...

从“安全密码”到测试体系:Gitee Test 赋能关键领域软件质量保障

关键领域软件测试的"安全密码"&#xff1a;Gitee Test如何破解行业痛点 在数字化浪潮席卷全球的今天&#xff0c;软件系统已成为国家关键领域的"神经中枢"。从国防军工到能源电力&#xff0c;从金融交易到交通管控&#xff0c;这些关乎国计民生的关键领域…...

C++实现分布式网络通信框架RPC(2)——rpc发布端

有了上篇文章的项目的基本知识的了解&#xff0c;现在我们就开始构建项目。 目录 一、构建工程目录 二、本地服务发布成RPC服务 2.1理解RPC发布 2.2实现 三、Mprpc框架的基础类设计 3.1框架的初始化类 MprpcApplication 代码实现 3.2读取配置文件类 MprpcConfig 代码实现…...

软件工程 期末复习

瀑布模型&#xff1a;计划 螺旋模型&#xff1a;风险低 原型模型: 用户反馈 喷泉模型:代码复用 高内聚 低耦合&#xff1a;模块内部功能紧密 模块之间依赖程度小 高内聚&#xff1a;指的是一个模块内部的功能应该紧密相关。换句话说&#xff0c;一个模块应当只实现单一的功能…...

欢乐熊大话蓝牙知识17:多连接 BLE 怎么设计服务不会乱?分层思维来救场!

多连接 BLE 怎么设计服务不会乱&#xff1f;分层思维来救场&#xff01; 作者按&#xff1a; 你是不是也遇到过 BLE 多连接时&#xff0c;调试现场像网吧“掉线风暴”&#xff1f; 温度传感器连上了&#xff0c;心率带丢了&#xff1b;一边 OTA 更新&#xff0c;一边通知卡壳。…...

Netty自定义协议解析

目录 自定义协议设计 实现消息解码器 实现消息编码器 自定义消息对象 配置ChannelPipeline Netty提供了强大的编解码器抽象基类,这些基类能够帮助开发者快速实现自定义协议的解析。 自定义协议设计 在实现自定义协议解析之前,需要明确协议的具体格式。例如,一个简单的…...

初探用uniapp写微信小程序遇到的问题及解决(vue3+ts)

零、关于开发思路 (一)拿到工作任务,先理清楚需求 1.逻辑部分 不放过原型里说的每一句话,有疑惑的部分该问产品/测试/之前的开发就问 2.页面部分(含国际化) 整体看过需要开发页面的原型后,分类一下哪些组件/样式可以复用,直接提取出来使用 (时间充分的前提下,不…...

Axure Rp 11 安装、汉化、授权

Axure Rp 11 安装、汉化、授权 1、前言2、汉化2.1、汉化文件下载2.2、windows汉化流程2.3、 macOs汉化流程 3、授权 1、前言 Axure Rp 11官方下载链接&#xff1a;https://www.axure.com/downloadthanks 2、汉化 2.1、汉化文件下载 链接: https://pan.baidu.com/s/18Clf…...