多态(个人学习笔记黑马学习)

news2024/11/26 0:52:24

多态分为两类

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

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

  • 静态多态的函数地址早绑定 · 编译阶段确定函数地址
  • 动态多态的函数地址晚绑定 · 运行阶段确定函数地址

1、基本语法

#include <iostream>
using namespace std;
#include <string>

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

//猫类 
class Cat :public Animal {
public:

	//重写 函数返回值类型 函数名 参数列表 完全相同
	void speak() {
		cout << "小猫在说话" << endl;
	}
};

//狗类
class Dog :public Animal {
public:
	void speak() {
		cout << "小狗在说话" << endl;
	}
};

//执行说话的函数
//地址早绑定 在编译阶段确定函数地址
//如果想执行让猫说话,那么这个函数地址就不能提前绑定,需要在运行阶段进行绑定,地址晚绑定

//动态多态满足条件
//1、有继承关系
//2、子类重写父类的虚函数

//动态多态的使用
//父类的指针或者引用 指向子类对象

void doSpeak(Animal& animal) {  //Animal& animal = cat;
	animal.speak();
}

void test01() {
	Cat cat;
	doSpeak(cat);

	Dog dog;
	doSpeak(dog);
}

int main() {

	test01();


	system("pause");
	return 0;
}

2、案例1:计算器类

案例描述:
分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类

#include <iostream>
using namespace std;
#include <string>

//普通写法
class Calculator {
public:

	int getResult(string oper) {
		if (oper == "+") {
			return m_Num1 + m_Num2;
		}
		else if (oper == "-") {
			return m_Num1 - m_Num2;
		}
		else if (oper == "*") {
			return m_Num1 * m_Num2;
		}
		//如果想扩展新的功能,需要修改源码
		//在真正的开发中 提倡 开闭原则
		//开闭原则:对扩展进行开发,对修改进行关闭
	}

	int m_Num1;//操作数1
	int m_Num2;//操作数2
};

void test01() {

	//创建计算器对象
	Calculator c;
	c.m_Num1 = 10;
	c.m_Num2 = 10; 

	cout << c.m_Num1 << "+" << c.m_Num2 << "=" << c.getResult("+") << endl;
	cout << c.m_Num1 << "-" << c.m_Num2 << "=" << c.getResult("-") << endl;
	cout << c.m_Num1 << "*" << c.m_Num2 << "=" << c.getResult("*") << endl;
}

//利用多态实现计算器
//多态好处:
// 1、组织结构清晰
// 2、可读性强
// 3、对前期和后期拓展和维护性高
//实现计算器抽象类
class AbstractCalculator {
public:
	virtual int getResult() {
		return 0;
	}

	int m_Num1;
	int m_Num2;
};

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

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

//乘法计算器类
class MulCalculator :public AbstractCalculator {
public:
	int getResult() {
		return m_Num1 * m_Num2;
	}
};

void test02() {

	//多态使用
	//父类指针或者引用指向子类对象
	// 
	//加法运算
	AbstractCalculator* abc = new AddCalculator;
	abc->m_Num1 = 100;
	abc->m_Num2 = 100;

	cout << abc->m_Num1 << "+" << abc->m_Num2 << "=" << abc->getResult() << endl;
	//用完后记得销毁
	delete abc;
	abc = NULL;		

	//减法运算
	abc = new SubCalculator;
	abc->m_Num1 = 100;
	abc->m_Num2 = 100;

	cout << abc->m_Num1 << "-" << abc->m_Num2 << "=" << abc->getResult() << endl;
	delete abc;
	abc = NULL;


	//乘法运算
	abc = new MulCalculator;
	abc->m_Num1 = 100;
	abc->m_Num2 = 100;

	cout << abc->m_Num1 << "*" << abc->m_Num2 << "=" << abc->getResult() << endl;
	delete abc;
	abc = NULL;


}

int main() {

	//test01();
	test02();

	system("pause");
	return 0;
}


3、纯虚函数和抽象类

#include <iostream>
using namespace std;
#include <string>

class Base {
public:
	//纯虚函数
	//只要有一个纯虚函数,这个类称为抽象类
	//抽象类特点:
	//1、无法实例化对象
	//2、抽象类的子类 必须要重写父类中的纯虚函数,否则也属于抽象类
	virtual void func() = 0;
};

class Son :public Base {
public:
	virtual void func() {
		cout << "fun()函数调用" << endl;
	}
};

void test01() {
	//Base b;//抽象类无法实例化对象
	//new Base;//抽象类无法实例化对象

	//Son s;//子类必须重写父类中的纯虚函数,否则无法实例化对象
	Base* base = new Son;
	base->func();

}

int main() {



	system("pause");
	return 0;
}

4、案例2:制作饮品

案例描述:
制作饮品的大致流程为: 煮水 冲泡 倒入杯中 加入辅料
利用多态技术实现本案例,提供抽象制作饮品基类,提供子类制作咖啡和茶叶

#include <iostream>
using namespace std;
#include <string>

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) {//AbstractDrinking* abs=new Coffee;
	abs->makeDrink();
	delete abs;//释放		
}


void test01() {

	//制作咖啡
	doWork(new Coffee);

	cout << "---------------" << endl;
	//制作茶叶
	doWork(new Tea);
	

}

int main() {
	test01();


	system("pause");
	return 0;
}


5、虚析构和纯虚析构 

虚析构和纯虚析构共性

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

虚析构和纯虚析构区别:

  • 如果是纯虚析构,该类属于抽象类,无法实例化对象
#include <iostream>
using namespace std;
#include <string>

class Animal {
public:
	Animal() {
		cout << "Animal构造函数调用" << endl;
	}

	//利用虚析构可以解决 父类指针释放子类对象时不干净的问题
	/*virtual ~Animal()
	{
		cout<< "Animal虚析构函数调用" << endl;
	}*/
	//纯虚析构 需要声明也需要实现
	virtual ~Animal() = 0;

	//纯虚函数
	virtual void speak() = 0;
};
Animal:: ~Animal() {
	cout << "Animal纯虚析构函数调用" << endl;
}

class Cat :public Animal {
public:

	Cat(string name) {
		cout << "Cat构造函数调用" << endl;
		m_Name= new string(name);

	}
	virtual void speak() {
		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->speak();
	//父类指针在析构时候 不会调用子类中析构函数,导致子类如果有堆区属性,出现内存泄露
	delete animal;  
}

int main() {

	test01();

	system("pause");
	return 0;
}

6、案例三:电脑组装

  • 电脑主要组成部件为 CPU (用于计算),显卡 (用于显示),内存条 (用于存储)
  • 将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商
  • 创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口
  • 测试时组装三台不同的电脑进行工作
#include <iostream>
using namespace std;
#include <string>

//抽象不同零件类

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

};

//抽象显卡类
class VideoCard {
public:
	//抽象的显示函数
	virtual void display() = 0;

};

//抽象内存条类
class Memory {
public:
	//抽象的存储函数
	virtual void storage() = 0;

};


//电脑类
class Computer {
public:
	Computer(CPU* cpu, VideoCard* vc, Memory* mem) {
		m_cpu = cpu;
		m_vc = vc;
		m_mem = mem;
	}

	//提供工作的函数
	void work() {

		//让零件工作起来,调用接口
		m_cpu->calculate();

		m_vc->display();

		m_mem->storage();
	}

	//提供析构函数 释放3个电脑零件
	~Computer() {

		//释放cpu零件
		if (m_cpu != NULL) {
			delete m_cpu;
			m_cpu = NULL;
		}

		//释放显卡零件
		if (m_vc != NULL) {
			delete m_vc;
			m_vc = NULL;
		}

		//释放内存条零件
		if (m_mem != NULL) {
			delete m_mem;
			m_mem = NULL;
		}
	}

private:
	CPU* m_cpu;//CPU的零件指针
	VideoCard* m_vc;//显卡零件指针
	Memory* m_mem;//内存条零件指针
};

//具体厂商
//Intel厂商
class IntelCPU :public CPU {
public:
	virtual void calculate() {
		cout << "Intel的CPU开始计算了" << endl;
	}
};

class IntelVideoCard :public VideoCard {
public:
	virtual void display() {
		cout << "Intel的显卡开始显示了" << endl;
	}
};

class IntelMemory :public Memory {
public:
	virtual void storage() {
		cout << "Intel的内存条开始存储了" << endl;
	}
};


//Lenvo厂商
class LenvoCPU :public CPU {
public:
	virtual void calculate() {
		cout << "Lenvo的CPU开始计算了" << endl;
	}
};

class LenvoVideoCard :public VideoCard {
public:
	virtual void display() {
		cout << "Lenvo的显卡开始显示了" << endl;
	}
};

class LenvoMemory :public Memory {
public:
	virtual void storage() {
		cout << "Lenvo的内存条开始存储了" << endl;
	}
};


void test01() {

	//第一台电脑零件
	CPU* intelCpu = new IntelCPU;
	VideoCard* intelCard = new IntelVideoCard;
	Memory* intelMem = new IntelMemory;

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


	cout << "---------------------" << endl;
	cout << "第二台电脑开始工作" << endl;
	//创建第二台电脑
	Computer* computer2 = new Computer(new LenvoCPU,new LenvoVideoCard,new LenvoMemory);
	computer2->work();
	delete computer2;

	cout << "---------------------" << endl;
	cout << "第三台电脑开始工作" << endl;
	//创建第三台电脑
	Computer* computer3 = new Computer(new LenvoCPU, new IntelVideoCard, new LenvoMemory);
	computer3->work();
	delete computer3;
}


int main() {

	test01();

	system("pause");
	return 0;
}

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

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

相关文章

IMX6ULL移植篇-uboot源码主要文件说明

一. uboot 源码分析前提 由于 uboot 会使用到一些经过编译才会生成的文件&#xff0c;因此&#xff0c;我们在分析 uboot的时候&#xff0c;需要先编译一下 uboot 源码工程。 这里所用的开发板是 nand-flash 版本。 本文学习续上一篇文章&#xff0c;如下&#xff1a; IMX6U…

zabbix 自动发现

哈喽大家好&#xff0c;我是咸鱼 昨天老大让我初始化一批服务器&#xff0c;吭哧吭哧弄完之后需要把这批机器添加到 zabbix 上去 但是我发现一台一台添加效率好低&#xff0c;而且特别繁琐&#xff0c;当时我没有想出有什么好的方法&#xff0c;今天上网搜了一下相关资料之后…

Android Automotive概述

Android开发者的新赛道 在智能手机行业初兴起时&#xff0c;包括BAT在内许多传统互联网企业都曾布局手机产业&#xff0c;但是随着手机市场的基本定型&#xff0c;造车似乎又成了各大资本下一个追逐的方向。百度、小米先后宣布造车&#xff0c;阿里巴巴则与上汽集团共同投资创…

Camunda 7.x 系列【48】候选用户和用户组

有道无术,术尚可求,有术无道,止于术。 本系列Spring Boot 版本 2.7.9 本系列Camunda 版本 7.19.0 源码地址:https://gitee.com/pearl-organization/camunda-study-demo 文章目录 1. 概述2. 案例演示1. 概述 在之前的文档中,用户任务都是基于的Assignee设置固定的执行人…

车联网仿真工具Veins学习1

准备条件 假如你是一个小白&#xff0c;先找到相关的参考资料&#xff08;已根据上一篇博客安装好Veins&#xff09;&#xff0c;主要是官方文档和相关的博客&#xff0c;官方提供了一个example&#xff0c;我找到的资料如下&#xff1a; Frequently Asked Questions (FAQ) O…

C++ - 多态的实现原理

前言 本博客主要介绍C 当中 多态语法的实现原理&#xff0c;如果有对 多态语法 有疑问的&#xff0c;请看下面这篇博客&#xff1a; 探究&#xff0c;为什么多态的条件是那样的&#xff08;虚函数表&#xff09; 首先&#xff0c;调用虚函数必须是 父类的 指针或 引用&#xf…

py脚本解决ArcGIS Server服务内存过大的问题

在一台服务器上&#xff0c;使用ArcGIS Server发布地图服务&#xff0c;但是地图服务较多&#xff0c;在发布之后&#xff0c;服务器的内存持续处在95%上下的高位状态&#xff0c;导致服务器运行状态不稳定&#xff0c;经常需要重新启动。重新启动后重新进入这种内存高位的陷阱…

并行和并发的区别

从操作系统的角度来看&#xff0c;线程是CPU分配的最小单位。 并行就是同一时刻&#xff0c;两个线程都在执行。这就要求有两个CPU去分别执行两个线程。并发就是同一时刻&#xff0c;只有一个执行&#xff0c;但是一个时间段内&#xff0c;两个线程都执行了。并发的实现依赖于…

立晶半导体Cubic Lattice Inc 专攻音频ADC,音频DAC,音频CODEC,音频CLASS D等CL7016

概述&#xff1a; CL7016是一款高保真USB Type-C兼容音频编解码芯片。可以录制和回放有24比特音乐和声音。内置回放通路信号动态压缩&#xff0c; 最大42db录音通路增益&#xff0c;PDM数字麦克风&#xff0c;和立体声无需电容耳机驱动放大器。 5V单电源供电。兼容USB 2.0全速工…

三分法,伟大无比的二分法扩展,本节带部分数论问题。

一&#xff0c;引导简介 简单的来看三分法实际就是二分法的另一种扩展&#xff0c;可以完全的看成二分法&#xff0c;我们介绍几个特殊的点&#xff0c;才能使用这个解法来进行相关的算法求解&#xff1a;求解单调性改变的点&#xff0c;在本个区间中只有一个导数为 0 的点&…

基于qt软件的网上聊天室软件

1.服务器: 1).功能: 用于创建一个客户端&#xff0c;通过文本编辑器来获得端口号&#xff0c;根据获得的端口号创建服务器&#xff0c;等待客户端连接 创建成功会提示服务器创建成功 在收到客户端发送的信息时&#xff0c;把这条信息发送给其他所有客户端&#xff0c;实现群…

力扣(LeetCode)算法_C++——有效的数独

请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 &#xff0c;验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#xff08;请参考示例图&#xff09; …

vue3集成jsoneditor

一、背景 之前在做录制回放平台的时候&#xff0c;需要前端展示子调用信息&#xff0c;子调用是一个请求列表数组结构&#xff0c;jsoneditor对数组的默认展示结构是[0].[1].[2]..的方式&#xff0c;为了达到如下的效果&#xff0c;必须用到 onNodeName的钩子函数&#xff0c;…

使用Minifilter过滤驱动保护文件

代码如下: 可以保护拓展名.com文件不被删除、重命名、读写、可执行。#include <ntifs.h> #include <ntstrsafe.h> #include <fltKernel.h> static UNICODE_STRING ProtectedExtention RTL_CONSTANT_STRING(L"com"); //卸载回调 PFLT_FILTER gFile…

如何写一个可以找到工作的简历不至于太烂

简历是自己的一个很重要的标签&#xff0c;是获得面试的敲门砖&#xff0c;简历是要时常更新的&#xff0c;否则会错过一些机会。简历也是给自己的正反馈。 方法 ● 模仿&#xff0c;例如Boss&#xff0c;拉钩下面都给你一个案例模板供你参考&#xff0c;但是我觉得其实参考性…

基础算法--二分查找

二分查找 算法原理 1. 简介 故事分享&#x1f3ec;&#xff1a; 有一天小明到图书馆借了 N 本书&#xff0c;出图书馆的时候&#xff0c;警报响了&#xff0c;于是保安把小明拦下&#xff0c;要检查一下哪本书没有登记出借。小明正准备把每一本书在报警器下过一下&#xff0…

C语言基本知识

基础 第一个函数 argc代表参数个数argument count。argv代表参数value&#xff0c;第一个为放的是文件名&#xff0c;后面是传入的参数。 编译过程 预处&#xff1a;gcc -E hello.c -o hello.i编译&#xff1a;gcc -S hello.c(.i) -o hello.s汇编&#xff1a;gcc -c hello.c…

2023年09月数据库流行度最新排名

点击查看最新数据库流行度最新排名&#xff08;每月更新&#xff09; 2023年09月数据库流行度最新排名 TOP DB顶级数据库索引是通过分析在谷歌上搜索数据库名称的频率来创建的 一个数据库被搜索的次数越多&#xff0c;这个数据库就被认为越受欢迎。这是一个领先指标。原始数…

C语言“牵手”1688商品详情数据方法,1688商品详情API接口,1688API申请指南

1688是中国最大的自营式电商企业&#xff0c;在线销售计算机、手机及其它数码产品、家电、汽车配件、服装与鞋类、奢侈品、家居与家庭用品、化妆品与其它个人护理用品、食品与营养品、书籍与其它媒体产品、母婴用品与玩具、体育与健身器材以及虚拟商品等。 1688平台的商品详情…

route命令小结

Destination: 如果不满足该列的任何一个ip,则走默认的default Gataway: *是 不指定gateway.有的系统是0.0.0.0,与*意义相同 Genmask: 0.0.0.0是不指定掩码, 255.255.0.0掩码了16位,172.17 开头的ip,会走这个网关 255.255.255.0掩码了16位,192.168.0 开头的ip都会走这个网关 当是…