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();
}

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

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

相关文章

Java事件机制简介 内含面试题

面试题分享 云数据解决事务回滚问题 点我直达 2023最新面试合集链接 2023大厂面试题PDF 面试题PDF版本 java、python面试题 项目实战:AI文本 OCR识别最佳实践 AI Gamma一键生成PPT工具直达链接 玩转cloud Studio 在线编码神器 玩转 GPU AI绘画、AI讲话、翻译,GPU点亮…

用go实现一个循环队列

目录 队列数组队列的“假溢出”现象循环队列三种判断队列空和满的方法无下标&#xff08;链式&#xff09;有下标&#xff08;顺序&#xff09;长度标记 go用顺序表实现一个循环队列队列的链式存储结构 队列 队列&#xff08;queue&#xff09;是只允许在一端进行插入操作&…

TSN时间敏感网络

目录 时间敏感网络介绍 子协议介绍 时间同步 IEEE802.1AS 调度和流量整形 IEEE802.1Q IEEE802.1Qbv IEEE802.1cr IEEE802.1Qbu IEEE802.1Qch IEEE802.1Qav IEEE802.1Qcc 纠错机制与安全 IEEE802.1Qci IEEE802.1CB IEEE802.1Qca 参考 时间敏感网络介绍 TSN(Tim…

【技术分享】RK Android11系统SD卡启动方法

本文基于Purple Pi OH 3566主板&#xff0c;介绍Android11源码的修改&#xff0c;获得可从SD卡启动的Android11系统镜像。 Purple Pi OH作为一款兼容树莓派的开源主板&#xff0c;采用瑞芯微RK3566 (Cortex-A55) 四核64位超强CPU,主频最高达1.8 GHz,算力高达1Tops&#xff0c;…

C#,数值计算——多项式微分(Binomial Deviates)的计算方法与源程序

1 文本格式 using System; namespace Legalsoft.Truffer { /// <summary> /// 二项式偏差 /// Binomial Deviates /// </summary> public class Binomialdev : Ran { private double pp { get; set; } private double p…

docker 安装 Node-RED

Node-RED 是构建物联网应用程序的一个强大工具&#xff0c;使用可视化编程方法&#xff0c;连接起来执行任务。而homeassistant是家居智慧中枢&#xff0c;本文介绍如何安装Node-RED及HASS的插件 1、拉取镜像 docker pull nodered/node-red # 2、部署镜像 创建目录 mkidr -…

状态管理Pinia

Vue3 状态管理 - Pinia 1. 什么是Pinia Pinia 是 Vue 的专属的最新状态管理库 &#xff0c;是 Vuex 状态管理工具的替代品 2. 手动添加Pinia到Vue项目 后面在实际开发项目的时候&#xff0c;Pinia可以在项目创建时自动添加&#xff0c;现在我们初次学习&#xff0c;从零开始…

D. Choosing Capital for Treeland

Problem - 219D - Codeforces 问题描述&#xff1a;Treeland国有 n 个城市, 这 n 个城市连接成了一棵树, 靠单向道路相连, 现在政府想要选择一个城市作为首都, 条件是首都必须能到达其他所有城市, 现在我们不得不将一些道路反转方向, 记反转的条数为 k 条, 我们要找到所有使 k…

DEFORMABLE DETR: DEFORMABLE TRANSFORMERS FOR END-TO-END OBJECT DETECTION (论文解析)

DEFORMABLE DETR: DEFORMABLE TRANSFORMERS FOR END-TO-END OBJECT DETECTION 摘要1 介绍2 相关工作3 重新审视 Transformers 和 DETR4 方法4.1 用于端到端目标检测的可变形transformer4.2 Deformable Detr的其他改进和变型5 实验5.1 和DETR 比较5.2 消融实验5.3 与最先进方法的…

构建知识库:一文解决跨平台科研文献及笔记同步问题

文章目录 需求及目标现有方案调研文献管理方案云存储方案Markdown编辑器Windows端Ipad端 图床管理方案 最终方案操作流程最后 作为一个十级懒人&#xff0c;要么躺着要么在探寻提效工具的路上。 开始打工生涯之后&#xff0c;除了正常工作时间&#xff0c;总想利用业余时间提升…

vscode调试程序设置

主要设置和json内容如下&#xff1a; cpp_properties.json内容&#xff1a; {"configurations": [ //C intellisense插件需要这个文件&#xff0c;主要是用于函数变量等符号的只能解析{"name": "Win32","includePath": ["${work…

【IC设计】Chisel开发环境搭建

首先安装一个Ubuntu的虚拟机 然后给Ubuntu换个镜像&#xff0c;方便下载 注意换源后使用apt-get update更新下 安装vim&#xff08;可以不做&#xff09; 这里安装Vim是我感觉Ubuntu自带的vi编辑器似乎有问题&#xff0c;因为我按i进入【插入模式】并没有提示&#xff0c;所以…

Python+Requests+Pytest+Excel+Allure 接口自动化测试项目实战【框架之间的对比】

--------UnitTest框架和PyTest框架的简单认识对比与项目实战-------- 定义&#xff1a; Unittest是Python标准库中自带的单元测试框架&#xff0c;Unittest有时候也被称为PyUnit&#xff0c;就像JUnit是Java语言的标准单元测试框架一样&#xff0c;Unittest则是Python语言的标…

离子风蛇有什么作用?

离子风蛇的工作原理是通过内置的高压发生器升至高压电晕空气生成正负离子&#xff0c;再随风流覆盖至物体表面&#xff0c;从而中和其所带的正负静电电荷&#xff0c;这是一种用在工厂里面的工业设备&#xff0c;主要的作用是用来消除静电&#xff0c;其次还可以达到除尘和杀菌…

计算机专业毕业设计项目推荐01-生产管理系统(JavaSpringBoot+原生Js+Mysql)

生产管理系统&#xff08;JavaSpringBoot原生JsMysql&#xff09; **介绍****系统总体开发情况-功能模块****各部分模块实现****最后想说的****联系方式** 介绍 本系列(后期可能博主会统一为专栏)博文献给即将毕业的计算机专业同学们,因为博主自身本科和硕士也是科班出生,所以…

数据库原理及应用(MySQL)

建议大屏观看&#xff0c;避免格式错误&#xff0c;影响观感 目录 第一章 数据库系统概述 1.数据库系统概述 1.1.信息 1.2.数据 1.3.信息和数据之间的联系 1.4.数据库&#xff08;DB&#xff09; 1.5.数据库管理系统&#xff08;DBMS&#xff09; 1.6.数据库管理系统的…

Java多线程(一)多线程概要

多线程概要 多线程概要 什么是进程&#xff1f; 进程的特点&#xff1a; 什么是多线程 多线程编程&#xff1a; 创建线程 1.继承 Thread 类 2.实现 Runnable 接口 多线程的优势 中断问题&#xff1a; 1. 通过共享的标记来进行沟通 2. 调用 interrupt() 方法来通知 …

Python散点图

散点图 散点图是指在回归分析中&#xff0c;数据点在直角坐标系平面上的分布图&#xff0c;散点图表示因变量随自变量而变化的大致趋势&#xff0c;据此可以选择合适的函数对数据点进行拟合。用两组数据构成多个坐标点&#xff0c;考察坐标点的分布&#xff0c;判断两变量之间…

显示器鼠标滚动时或者拖拽文字变为绿色

新电脑&#xff0c;新显示器&#xff0c;看文章时滚动鼠标滑轮&#xff0c;文字颜色就变为绿色。 拖住文本文档或者浏览器等有文字的窗口&#xff0c;文字也会变为绿色。 静止时一点儿问题没有。 以下视频展示滚动和拖拽的操作&#xff0c;视频看不出变色&#xff0c;只参考…

科技云报道:AI时代,对构建云安全提出了哪些新要求?

科技云报道原创。 随着企业上云的提速&#xff0c;一系列云安全问题也逐渐暴露出来&#xff0c;云安全问题得到重视&#xff0c;市场不断扩大。 Gartner 发布“2022 年中国 ICT 技术成熟度曲线”显示&#xff0c;云安全已处于技术萌芽期高点&#xff0c;预期在2-5年内有望达到…