C++核心编程--多态篇

news2024/11/30 12:46:44

4.7、多态

4.7.1、多态的基本概念

多态是C++面向对象三大特征之一

多态分为两类

  • 静态多态:函数重载和运算符重载属于静态多态,复用函数名
  • 动态多态:派生类和虚函数实现运行时多态

静态多态和动态多态区别:

  • 静态多态的函数地址早绑定 - 编译阶段确定函数地址
  • 动态多态的函数地址晚绑定 - 运行阶段确定函数地址
  1. 动态多态满足条件
    • 有继承关系
    • 子类重写父类的虚函数
  2. 动态多态使用
    • 父类的指针或引用 指向子类对象

重写:函数返回值类型 函数名 参数列表 完全一致称为重写

class Animal {
public:
	//虚函数
	virtual void  spreak() {
		cout << "动物在说话" << endl;	
	}
};

class Cat :public Animal {
public:
	void spreak() {
		cout << "猫在说话" << endl;
	}
};

class Dog : public Animal {
public:
	void spreak() {
		cout << "狗在说话" << endl;
	}
};

//执行说话的函数
//地址早绑定 在编译阶段确定函数地址
//如果想让猫说话,就不能提前绑定,云瑶在运行阶段绑定,及地址晚绑定 
void doSpreak(Animal &animal) {
	animal.spreak();
}

void test01() {
	Cat cat;
	doSpreak(cat);
	Dog dog;
	doSpreak(dog);
}
4.7.2、多态案例-计算器类
  • 多态的优点:
    • 代码组织结构清晰
    • 可读性强
    • 利于前期和后期扩展以及维护
//实现计算机的抽象类
class AbstractCalculator {
public:
	virtual int getResult() {
		return 0;
	}

	int m_Num1,m_Num2;
};

//减法计算机类
class SubCalculator :public AbstractCalculator {
public:
	int getResult() {
		return m_Num1 - m_Num2;
	}
};
//加法计算机类
class AddCalculator :public AbstractCalculator {
public:
	int getResult() {
		return m_Num1 + m_Num2;
	}
};

void test01() {
	
	AbstractCalculator *a1 = new AddCalculator;
	a1->m_Num1 = 100;
	a1->m_Num2 = 200;
	cout << a1->m_Num1 << " + " << a1->m_Num2 << " = " << a1->getResult() << endl;
	//回收a1
	delete a1;

	a1 = new SubCalculator();
	a1->m_Num1 = 200;
	a1->m_Num2 = 100;
	cout << a1->m_Num1 << " - " << a1->m_Num2 << " = " << a1->getResult() << endl;
	//回收a1
	delete a1;
}

image-20230930093236142

4.7.3、纯虚函数和抽象类

在多态中,通常父类中虚函数的实现是毫无意义的,主要都是效用子类重写的内容

因此可以将虚函数改为纯虚函数

纯虚函数语法:virtual 返回值类型 函数名 (参数列表) = 0;

当类中有了纯虚函数,这个类也称为抽象类

抽象类特点:

  • 无法实例化对象
  • 子类必须重写抽象类中的纯虚函数,否则也属于抽象类
//纯虚函数和抽象类
class Base {
public:
	virtual void func() = 0;
};

//1、纯虚函数无法实例对象
//void test01() {
//	Base b;
//	new Base;
//}

//2、子类必须重写抽象类中的纯虚函数,否则也属于抽象类
//class Son : public Base {
//public:
//
//};
//
//void test02() {
//	Son s;
//}

//3、正确写法
class Son : public Base {
public:
	virtual void func() {
		cout << "func函数被调用" << endl;
	};
};

void test03() {
	Base* base = new Son;
	base->func();
}
4.7.4、多态案例二 - 制作饮品

案例描述:

制作饮品时大致的流程为: 煮水 - 冲泡 - 导入杯中 - 加入辅料

利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡喝茶叶

image-20230930102207998

//饮品抽象类
class MakeDreak {
public:
	virtual void Boil() = 0;
	virtual void Rush() = 0;
	virtual void Fall() = 0;
	virtual void Add() = 0;

	void markDrink() {
		Boil();
		Rush();
		Fall();
		Add();
	};
};

class Coffee : public MakeDreak {
	virtual void Boil() {
		cout << "煮水" << endl;
	}
	virtual void Rush() {
		cout << "冲泡咖啡" << endl;
	}
	virtual void Fall() {
		cout << "倒入杯中" << endl;
	}
	virtual void Add() {
		cout << "加糖和牛奶" << endl;
	}
};

class Tea : public MakeDreak {
	virtual void Boil() {
		cout << "煮水" << endl;
	}
	virtual void Rush() {
		cout << "冲泡茶叶" << endl;
	}
	virtual void Fall() {
		cout << "倒入杯中" << endl;
	}
	virtual void Add() {
		cout << "柠檬" << endl;
	}
};

//制作函数
void doWork(MakeDreak* abs) {
	abs->markDrink();
}

void test01() {
	doWork(new Coffee);
	cout << "-----------------" << endl;
	doWork(new Tea);
}
4.7.5、虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码

解决方法:将父类中的析构函数改为虚析构或者纯虚析构

虚析构和纯虚析构共性:

  • 可以解决父类指针释放子类对象
  • 都需要有具体的函数实现

虚析构和纯虚析构区别:

  • 如果是纯虚析构,该类属于抽象类,无法实例化对象

虚析构语法:

virtual ~类名(){}

纯虚析构语法:

virtual ~类名() = 0

类名::~类名(){}

class Animal {
public:
	Animal() {
		cout << "Animal构造函数被调用" << endl;
	}
	//利用虚析构可以解决 父类指针释放子类对象
	virtual ~Animal()
	{
		cout << "Animal析构函数被调用" << endl;
	}

	//纯虚函数
	virtual void spreak()  = 0;

};

class Cat : public Animal {
public:
	Cat(string name) {
		cout << "Cat构造函数调用" << endl;
		m_Name =  new string(name);
	}
	virtual void spreak() {
		cout << *m_Name<<"猫正在说话" << endl;
	}
	~Cat()
	{
		if (m_Name !=NULL)
		{
			cout << "Cat析构函数调用" << endl;
			delete m_Name;
			m_Name = NULL;
		}
	}
	string *m_Name;
};

void test01() {
	Animal* animal = new Cat("Tom");
	animal->spreak();
	//父类指针在析构时候,不会调用子类中析构函数,
	//导致子类如果有堆区属性,出现内存泄露
	delete animal;
}
4.7.6、多态案例三 - 电脑组装

案例描述:

电脑主要组成部件为CPU (用于计算),显卡(用于显示),内存条(用于存储)将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Inter厂商和Lenovo厂商创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口

测试时组装三台不同的电脑进行工作

//cpu类
class CPU {
public:
	//抽象的计算机函数
	virtual void caculate() = 0;
};

//显卡类
class Graphics {
public:
	//抽象的显卡
	virtual void show() = 0;
};

//内存类
class RAM {
public:
	virtual void save() = 0;
};


class Computer {
public:
	Computer(CPU* cpu, Graphics* gpu, RAM* ram) {
		m_cpu = cpu;
		m_gpu = gpu;
		m_ram = ram;
	}

	void work() {
		m_cpu->caculate();
		m_gpu->show();
		m_ram->save();
	}
	//提供析构函数 释放3个电脑零件
	~Computer()
	{
		//释放cpu
		if (m_cpu!=NULL)
		{
			delete m_cpu;
			m_cpu = NULL;
		}
		//释放显卡
		if (m_gpu !=NULL)
		{
			delete m_gpu;
			m_gpu = NULL;
		}
		//释放内存条
		if (m_ram !=NULL)
		{
			delete m_ram;
			m_ram = NULL;
		}
	}
private:
	CPU* m_cpu; //CPU的零件指针
	Graphics* m_gpu;
	RAM* m_ram;
};

//Intel厂商
class IntelCPU : public CPU {
public:
	virtual void caculate() {
		cout << "Intel的CPU开始计算了" << endl;
	}
};
class IntelGPU : public Graphics {
public:
	virtual void show() {
		cout << "Intel的GPU开始显示了" << endl;
	}
};
class IntelRAM : public RAM {
public:
	virtual void save() {
		cout << "Intel的RAM开始存储了" << endl;
	}
};

//Lenovo厂商
class LenovoCPU : public CPU {
public:
	virtual void caculate() {
		cout << "AMD的CPU开始计算了" << endl;
	}
};
class LenovoGPU : public Graphics {
public:
	virtual void show() {
		cout << "AMD的GPU开始显示了" << endl;
	}
};
class LenovoRAM : public RAM {
public:
	virtual void save() {
		cout << "AMD的RAM开始存储了" << endl;
	}
};


void test01() {
	//第一台电脑零件
	CPU * intelCpu = new IntelCPU;
	Graphics * IntelGpu = new IntelGPU;
	RAM * IntelRam = new IntelRAM;

	//创建第一台电脑
	cout << "第一台电脑开始工作" << endl;
	Computer * computer1 = new Computer(intelCpu, IntelGpu, IntelRam);
	computer1->work();
	delete computer1;

	//第二台电脑
	cout << "---------------------" << endl;
	cout << "第二台电脑开始工作" << endl;
	Computer* computer2 = new Computer(new LenovoCPU, new LenovoGPU, new LenovoRAM);
	computer2->work();
	delete computer2;

	//第三台电脑
	cout << "---------------------" << endl;
	cout << "第三台电脑开始工作" << endl;
	Computer* computer3 = new Computer(new LenovoCPU, new IntelGPU, new LenovoRAM);
	computer3->work();
	delete computer3;


}

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

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

相关文章

Eclipse 主网即将上线迎空投预期,Zepoch 节点或成受益者?

目前&#xff0c;Zepoch 节点空投页面中&#xff0c;模块化 Layer2 Rollup 项目 Eclipse 出现在其空投列表中。 配合近期 Eclipse 宣布了其将由 SVM 提供支持的 Layer2 主网架构&#xff0c;并将在今年年底上线主网的消息后&#xff0c;不免引发两点猜测&#xff1a;一个是 Ecl…

springcloud:四、nacos介绍+启动+服务分级存储模型/集群+NacosRule负载均衡

nacos介绍 nacos是阿里巴巴提供的SpringCloud的一个组件&#xff0c;算是eureka的替代品。 nacos启动 安装过程这里不再赘述&#xff0c;相关安装或启动的问题可以见我的另一篇博客&#xff1a; http://t.csdn.cn/tcQ76 单价模式启动命令&#xff1a;进入bin目录&#xff0…

某房产网站登录RSA加密分析

文章目录 1. 写在前面2. 抓包分析3. 扣加密代码4. 还原加密 1. 写在前面 今天是国庆节&#xff0c;首先祝福看到这篇文章的每一个人节日快乐&#xff01;假期会老的这些天一直在忙事情跟日常带娃&#xff0c;抽不出一点时间来写东西。夜深了、娃也睡了。最近湖南开始降温了&…

SimpleCG动画示例--汉诺塔动画演示

前言 SimpleCG的使用方法在前面已经介绍了许多&#xff0c;有兴趣的同学如果有去动手&#xff0c;制作一些简单动画应该没多大问题的。所以这次我们来演示一下简单动画。我们刚学习C语言的递归函数时&#xff0c;有一个经典例子相信很多同学都写过&#xff0c;那就是汉诺塔。那…

【算法优选】双指针专题——壹

文章目录 &#x1f60e;前言&#x1f334;[移动零](https://leetcode.cn/problems/move-zeroes/)&#x1f6a9;题⽬描述&#xff1a;&#x1f6a9;算法思路&#x1f6a9;算法流程&#x1f6a9;代码实现 &#x1f340;[复写零](https://leetcode.cn/problems/duplicate-zeros/)&…

深入浅出线程池

一、线程 1、什么是线程 线程(thread)是操作系统能够进行运算调度的最小单位。它被包含在进程之中&#xff0c;是进程中的实际 运作单位。一条线程指的是进程中一个单一顺序的控制流&#xff0c;一个进程中可以并发多个线程&#xff0c;每条线 程并行执行不同的任务。 2、如…

2023-10-01 LeetCode每日一题(买卖股票的最佳时机)

2023-10-01每日一题 一、题目编号 121. 买卖股票的最佳时机二、题目链接 点击跳转到题目位置 三、题目描述 给定一个数组 prices &#xff0c;它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票&#xff0c;并选择在 未来的某一…

Python Appium 安卓自动化测试 基本使用 - Phone Spider

Python Appium 安卓自动化测试 基本使用 提示&#xff1a;这里可以添加系列文章的所有文章的目录&#xff0c;目录需要自己手动添加 例如&#xff1a;第一章 Python 机器学习入门之pandas的使用 文章目录 Python Appium 安卓自动化测试 基本使用前言一、环境安装1.1 Python Pip…

【接口技术】输入输出接口习题

1&#xff1a;图1所示电路PC/XT系统板上的接口控制电路的端口地址译码电路。写出8237A&#xff0c;8259A&#xff0c;8253&#xff0c;8255A的端口地址范围 解答&#xff1a; 【G1A非】和【G2B非】为低电平有效&#xff0c;因此A80&#xff0c;A90 74ls138中&#xff0c;是按…

python代码混淆与代码打包

0x00 背景 自己写的项目&#xff0c;又想保护源码&#xff0c;自己做个混淆是最方便的了。 0x01 实践 这里使用开源工具 GitHub - astrand/pyobfuscate: pyobfuscate&#xff0c;虽然git上才500多star&#xff0c;但是很好用。它的使用场景是混淆单个py文件。很多事物有开始就…

六、vpp 流表

草稿&#xff01;&#xff01;&#xff01; vpp node其实就是三个部分 1、plugin init 2、set command 3、function 实现功能&#xff0c;比如这里的流表 这里的function函数有个返回值&#xff0c;根据返回值决定下一个节点走哪里 flowtable_getinfo这里处理函数返回2 &#…

Nodejs沙箱逃逸

一、基本概念 JavaScript和Nodejs之间有什么区别 JavaScript用在浏览器前端&#xff0c;后来将Chrome中的v8引擎单独拿出来为JavaScript单独开发了一个运行环境&#xff0c;因此JavaScript也可以作为一门后端语言&#xff0c;写在后端&#xff08;服务端&#xff09;的JavaScr…

什么是博弈论?

什么是博弈&#xff1f;字面描述中&#xff0c;博弈由两个字构成&#xff1a;博 和 弈。博弈是一种双方&#xff08;多方&#xff09;的对抗&#xff08;比赛&#xff09;&#xff0c;对抗总是在一定的规则下进行&#xff0c;参与者必然会考虑应用相应的策略&#xff08;计谋&a…

防火墙基础之H3C防火墙分支与分支之间双向地址转换

分支与分支之间双向地址转换 原理概述&#xff1a; 防火墙&#xff08;英语&#xff1a;Firewall&#xff09;技术是通过有机结合各类用于安全管理​与筛选的软件和硬件​设备&#xff0c;帮助计算机网络于其内、外网之间构建一道相对隔绝的保护屏障&#xff0c;以保护用户资…

leetCode 376.摆动序列 动态规划 + 图解 + 状态转移

376. 摆动序列 - 力扣&#xff08;LeetCode&#xff09; 如果连续数字之间的差严格地在正数和负数之间交替&#xff0c;则数字序列称为 摆动序列 。第一个差&#xff08;如果存在的话&#xff09;可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。 例如…

SpringBoot整合RocketMQ笔记

SpringBoot版本为2.3.12.Release RocketMQ对比kafka 学习链接 https://zhuanlan.zhihu.com/p/335216381 代码实战 https://www.cnblogs.com/RedOrange/p/17401238.html Centos安装rocketmq https://blog.csdn.net/chuige2013/article/details/123783612 RocketMQ详细配置与…

竞赛 大数据疫情分析及可视化系统

文章目录 0 前言2 开发简介3 数据集4 实现技术4.1 系统架构4.2 开发环境4.3 疫情地图4.3.1 填充图(Choropleth maps)4.3.2 气泡图 4.4 全国疫情实时追踪4.6 其他页面 5 关键代码最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 大数据疫…

宝塔 php修改了php.ini配置不生效

最近在使用hypref&#xff0c;php的版本是7.4 服务器linux&#xff0c;用宝塔安装完php,并装完swoole插件后 安装了swoole后&#xff0c;需要在php.ini中修改一下配置文件 添加 swoole.use_shortnameOff 但是添加了&#xff0c;重启php,依然不生效 解决方法是&#xff1a; 同时…

数字时代古文的传承———云南文化瑰宝“爨文化“(我为家乡发声)

文章目录 前言⭐ "爨"意味着什么&#xff0c;究竟何为"爨文化"&#xff1f;⭐ 爨文化鲜明的特点1.经济生活2.政治生活3.文化艺术 ⭐ 数字时代古文的传承与传播1.藏品数字化2.建立数据库3.传播大众化 前言 爨文化是继古滇文化之后崛起于珠江正源南盘江流域…

[Linux] 6.VMware虚拟机网络配置

在VMware虚拟机下可以在虚拟网络编辑器看到三种模式 一、Bridged&#xff08;桥接模式&#xff09; 桥接模式就是将主机网卡与虚拟机虚拟的网卡利用虚拟网桥进行通信。 真机、虚拟机都有自己的ip地址&#xff0c;能互相通讯&#xff0c;而且能上网。 功能齐全&#xff0c;但…