C++类和对象(多态)

news2024/10/6 16:16:19

4.7多态

4.7.1多态的基本概念

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

多态分为两类

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

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

  1. 静态多态的函数地址早绑定–编译阶段确定函数地址
  2. 动态多态的函数地址晚绑定–运行阶段确定函数地址下面通过案例进行讲解多态
#include<iostream>
using namespace std;
//多态
//动物类
class Animal
{
public:
	//虚函数 virtual
	virtual void speak()
	{
		cout << "动物在说话" << endl;
	}
private:

};
//猫类
class Cat:public Animal
{
public:
	//重写 函数返回值 函数名 参数列表 完全相同  
	//子类关键字 virtual 可写可不写,
	void speak()
	{
		cout << "小猫在说话" << endl;
	}
private:

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

};

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

//动态多态满足条件
//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;
}

总结:

多态满足条件

  1. 有继承关系
  1. 子类重写父类中的虚函教

多态使用条件

  1. 父类指针或引用指向子类对象

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

 4.7.2多态案例——计算器类

案例描述:

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

多态的优点

  1. 代码组织结构清晰
  2. 可读性强
  3. 利于前期和后期的扩展以及维护
#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;
		}
		else if (oper == "/")
		{
			return m_Num1 / m_Num2;
		}
		else
		{
			cout << "输入有误,请重新输入“+、-、*、/”任意一个运算符" << endl;
		}
		//如果想扩展新的功能,需求修改源码。
		//在真实的开发中 提倡 开闭原则
		//开闭原则:对扩展进行开放,对修改进行关闭。 
	}
	int m_Num1;//操作数1
	int m_Num2;//操作数2
private:

};

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;
	cout << c.m_Num1 << "+" << c.m_Num2 << "=" << c.getResult("/") << endl;
}
//利用多态实现计算器
//多态好处
// 1、组织结构清晰
// 2、可读性强
// 3、对于前期和后期扩展的维护性高
//实现计算器抽象类
class AbstractCalculator
{
public:
	virtual int getResult()
	{
		return 0;
	}
	int m_Num1;//操作数1
	int m_Num2;//操作数2
};
//加法计算器类
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 = new SubCalculator;
	abc->m_Num1 = 100;
	abc->m_Num2 = 100;
	cout << abc->m_Num1 << "-" << abc->m_Num2 << "=" << abc->getResult() << endl;
	//用完后记得销毁
	delete abc;
	//乘法运算
	abc = new MulCalculator;
	abc->m_Num1 = 100;
	abc->m_Num2 = 100;
	cout << abc->m_Num1 << "*" << abc->m_Num2 << "=" << abc->getResult() << endl;
	//用完后记得销毁
	delete abc;
}

int main()
{
	//test01();
	test02();

	system("pause");
	return 0;
}

4.7.3纯虚函数和抽象类

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

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

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

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

抽象类特点:

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

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

void test01()
{
	//Base b;//抽象类是无法实例化对象
	//new Base;//抽象类是无法实例化对象
	//Son s;//子类必须重写父类中的纯虚函数,否则无法实例化对象。
	Base * base = new Son;//父类指针指向子类
	base->func();
}

int main()
{
	test01();

	system("pause");
	return 0;
}

4.7.4多态案例二-制作饮品

案例描述:

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

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

#include<iostream>
using namespace std;
//多态案例2 制作饮品
class AbstractDrinking
{
public:
	//煮水
	virtual void Boil() = 0;
	//冲泡
	virtual void Brew() = 0;
	//倒入杯中
	virtual void PourInCup() = 0;
	//加入辅助佐料
	virtual void PutSomething() = 0;
	//制作饮品
	void makeDrinking()
	{
		Boil();
		Brew();
		PourInCup();
		PutSomething();
	}
private:

};
//制作咖啡
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->makeDrinking();
	delete abs;//释放堆区数据
}

void test01()
{
	//制作咖啡
	doWork(new Coffee);
	cout << "-----------------" << endl;
	//制作茶叶
	doWork(new Tea);
}

int main()
{
	test01();

	system("pause");
	return 0;
}

4.7.5虚析构和纯虚析构

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

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

虚析构和纯虚析构共性

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

虚析构和纯虚析构区别

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

虚析构语法:

virtual ~类名(){}

纯虚析构语法:

virtual ~类名() = 0;

类名 : : ~ 类名(){}

(1)虚析构

#include<iostream>
using namespace std;
#include<string>
//虚析构
class Animal
{
public:
	Animal()
	{
		cout << "Animal构造函数调用" << endl;
	}
	//利用虚析构可以解决 父类指针释放子类对象时不干净的问题
	virtual ~Animal()//虚析构
	{
		cout << "Animal析构函数调用" << endl;
	}
	//纯虚函数
	virtual void speak() = 0;
private:

};
//创建子类——猫
class Cat : public Animal
{
public:
	Cat(string name)
	{
		cout << "Cat构造函数调用" << endl;
		m_Name = new string(name);
	}
	~Cat()
	{
		if (m_Name != NULL)
		{
			cout << "Cat析构函数调用" << endl;
			delete m_Name;
			m_Name = NULL;
		}
	}

	//纯虚函数
	virtual void speak()
	{
		cout << *m_Name << "小猫在说话" << endl;
	}
	string* m_Name;
private:

};

void test01()
{
	Animal* animal = new Cat("Tom");
	animal->speak();
	//父类指针在析构时候  不会调用子类中的析构函数,导致子类如果有堆区属性,出现内存泄露。
	delete animal;
	cout << "xxxxx" << endl;
}

int main()
{
	test01();

	system("pause");
	return 0;
}

(2)纯虚析构

#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;
private:

};
Animal::~Animal()//纯虚析构实现
{
	cout << "Animal析构函数调用" << endl;
}
//创建子类——猫
class Cat : public Animal
{
public:
	Cat(string name)
	{
		cout << "Cat构造函数调用" << endl;
		m_Name = new string(name);
	}
	~Cat()
	{
		if (m_Name != NULL)
		{
			cout << "Cat析构函数调用" << endl;
			delete m_Name;
			m_Name = NULL;
		}
	}

	//纯虚函数
	virtual void speak()
	{
		cout << *m_Name << "小猫在说话" << endl;
	}
	string* m_Name;
private:

};

void test01()
{
	Animal* animal = new Cat("Tom");
	animal->speak();
	//父类指针在析构时候  不会调用子类中的析构函数,导致子类如果有堆区属性,出现内存泄露。
	delete animal;
	cout << "xxxxx" << endl;
}

int main()
{
	test01();

	system("pause");
	return 0;
}

总结:

1.虚析构或纯虚析构就是用来解决通过父类指针释放子类对象

2.如果子类中没有堆区数据,可以不写为虚析构或纯虚析构

3.拥有纯虚析构函数的类也属于抽象类

4.7.6多态案例三-电脑组装

案例描述:

电脑主要组成部件为CPU(用于计算),显卡(用于显示),内存条(用于存储)

将每个零件封装出抽象基类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商

创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口

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

需求分析:

 具体实现:

#include<iostream>
using namespace std;
//抽象不同零件类
//抽象CPU类
class CPU
{
public:
	//抽象的计算函数
	virtual void calculate() = 0;
private:

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

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

};
//电脑类
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;
	}
};

// Lenovo厂商
class LenovoCPU :public CPU
{
public:
	virtual void calculate()
	{
		cout << "Lenovo的CPU开始计算了!" << endl;
	}
};
class LenovoVideoCard :public VideoCard
{
public:
	virtual void display()
	{
		cout << "Lenovo的显卡开始显示了!" << endl;
	}
};
class LenovoMemory :public Memory
{
public:
	virtual void storage()
	{
		cout << "Lenovo的内存条开始存储了!" << endl;
	}
};

void test01()
{
	//第一台电脑的零件
	CPU* intelCpu = new IntelCPU;
	VideoCard* intelCard = new IntelVideoCard;
	Memory* intelMem = new IntelMemory;
	//第一台电脑组装
	Computer* computer1 = new Computer(intelCpu, intelCard, intelMem);
	computer1 -> work();
	delete computer1;

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

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

int main()
{
	test01();

	system("pause");
	return 0;
}

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

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

相关文章

SpringBoot 如何使用 Tomcat 容器

SpringBoot 如何使用 Tomcat 容器 SpringBoot 是一种基于 Spring 框架的快速开发框架&#xff0c;它提供了许多功能和组件&#xff0c;其中包括了内置的 Tomcat 容器。在本文中&#xff0c;我们将介绍如何使用 SpringBoot 内置的 Tomcat 容器来运行 Web 应用程序。 SpringBoot…

ubuntu 18.04系统python3.6 升级到 python3.9

1.安装依赖库 sudo apt-get install build-essential libbz2-dev libssl-dev libreadline-dev libsqlite3-dev 2.下载 Python 3.9 源代码 wget https://www.python.org/ftp/python/3.9.5/Python-3.9.5.tar.xz # 解压 tar -xf Python-3.9.5.tar.xz 3.编译Python 3.9 cd Pytho…

Grounded-Segment-Anything本地部署

Grounded-Segment-Anything本地部署 1.本地部署源码1.1 克隆源码1.2 下载初始参数 2. 创建虚拟环境及配置3. 测试相关文件3.1 运行grounding_dino_demo.py文件3.2 运行grounded_sam_demo.py文件3.3 运行grounded_sam_simple_demo.py文件3.4 grounded_sam_inpainting_demo.py文件…

Spring Boot 如何使用 Jetty 容器

Spring Boot 如何使用 Jetty 容器 Jetty 是一款轻量级的 Java Web 容器&#xff0c;适用于各种规模的 Web 应用程序。在 Spring Boot 中&#xff0c;我们可以使用 Jetty 作为 Web 容器来处理 HTTP 请求和响应。本文将介绍 Spring Boot 如何使用 Jetty 容器&#xff0c;包括如何…

SpringBoot 如何使用 AOP 容器

SpringBoot 如何使用 AOP 容器 SpringBoot 是基于 Spring Framework 的一种快速开发框架&#xff0c;它提供了丰富的功能和组件&#xff0c;其中之一就是 AOP&#xff08;面向切面编程&#xff09;。AOP 是一种编程范式&#xff0c;可以将横向关注点&#xff08;如日志记录、事…

紫东太初:造一个国产大模型,需用多少篇高质量论文?

原创&#xff1a;谭婧 “视觉自监督算法这轮&#xff0c;你是不是没跟上&#xff1f;” 我面前这位年薪近七十万&#xff0c;互联网大厂AI算法工程师的好友&#xff0c; 他用一个反问句回答了我的关心&#xff1a; “自监督这个玩意咋跟上&#xff1f;” 他抬了抬头&#xff0c…

shell 拼接字符串,出现后面的字符串覆盖前面的字符串问题

字符串拼接语法 shell 拼接字符串语法很简单两个参数并排前后写上接口&#xff0c;如下 str1www str2bbb s t r 1 str1 str1str2 的结果未 wwwbbb 有时会出现如下问题&#xff1a; 在拼接字符串的时候&#xff0c;后面的字符串将前面的字符串覆盖了。 一、问题描述 shell的…

Unity3D:Game 视图

推荐&#xff1a;将 NSDT场景编辑器 加入你的3D工具链 3D工具集&#xff1a; NSDT简石数字孪生 Game 视图 从应用程序中的摄像机渲染 Game 视图。 该视图代表最终发布的应用程序。 需要使用一个或多个摄像机来控制玩家在使用应用程序时看到的内容。 有关摄像机的更多信息&#…

Github调研:开发者对生产力、协作和AI编码工具的看法

Datawhale干货 翻译&#xff1a;段秋阳 Datawhale成员 调研背景 GitHub首席产品官Inbal Shani如是说&#xff1a; 今天的开发人员所做的不仅仅是编写和发布代码&#xff0c;他们还需要熟练应用各种工具、环境和技术&#xff0c;包括生成人工智能编码工具这个新领域。但对开发人…

Python基础(14)——Python元组(tuple)详解

Python基础&#xff08;14&#xff09;——Python元组&#xff08;tuple&#xff09;详解 文章目录 Python基础&#xff08;14&#xff09;——Python元组&#xff08;tuple&#xff09;详解课程&#xff1a;元组目标一. 元组的应用场景二. 定义元组三. 元组的常见操作四. 总结…

vue进阶-vue-route

Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成&#xff0c;让用 Vue.js 构建单页应用变得轻而易举。 本章只做学习记录&#xff0c;详尽的内容一定要去官网查看api文档 Vue Router-Vue.js 的官方路由 1. 路由的基本使用 1.1 安装vue-router npm install vue-…

图解transformer中的自注意力机制

本文将将介绍注意力的概念从何而来&#xff0c;它是如何工作的以及它的简单的实现。 注意力机制 在整个注意力过程中&#xff0c;模型会学习了三个权重:查询、键和值。查询、键和值的思想来源于信息检索系统。所以我们先理解数据库查询的思想。 假设有一个数据库&#xff0c…

Django-带参数的路由编写(二)【用正则表达式匹配复杂路由】

在上一篇博文中&#xff0c;学习了“不用正则表达式匹配的简单带参数路由”&#xff0c;详情见链接&#xff1a; https://blog.csdn.net/wenhao_ir/article/details/131225388 本篇博文学习用“用正则表达式匹配复杂路由”。 简单的参数路由用库django.urls中的函数path()就可…

内涝监测系统如何助力城市防洪抗涝

近年来&#xff0c;各地内涝问题愈发严重&#xff0c;强降雨天气导致城市内涝已经屡见不鲜了&#xff0c;城市内涝不仅影响城市交通、居民生活&#xff0c;还可能对建筑物和基础设施造成损害&#xff0c;给城市运行带来重大风险。内涝治理除了要解决城市“里子”问题&#xff0…

【2023,学点儿新Java-11】基础案例练习:输出个人基础信息、输出心形 | Java中 制表符\t 和 换行符\n 的简单练习

前情回顾&#xff1a; 【2023&#xff0c;学点儿新Java-10】Java17 API文档简介&获取 |详解Java核心机制&#xff1a;JVM |详解Java内存泄漏与溢出 |Java优缺点总结 |附&#xff1a;GPT3.5-turbo问答测试【2023&#xff0c;学点儿新Java-09】Java初学者常会犯的错误总结与…

数据库SQL Server实验报告 之 SQL语言进行数据更新(6/8)

SQL语言进行数据更新 生命的本质是一场历练 实验目的及要求&#xff1a; 掌握如何使用sql语句进行插入、删除和更新操作。使用sql语句进行插入操作。使用sql语句进行删除操作。使用sql语句进行更新操作。使用各种查询条件完成指定的查询操作 实验内容及步骤&#xff1a…

计算机基础--->网络(2)【TCP、UDP、IP、ARP】

文章目录 TCP与UDP的区别TCP三次握手和四次挥手为什么要三次握手&#xff1f;第二次握手传回了ACK&#xff0c;为什么还要传回SYN&#xff1f;为什么要四次挥手&#xff1f;为什么不能将服务器发送的ACK和FIN合并起来&#xff0c;变成三次挥手&#xff1f;TCP如何保证传输的可靠…

推荐召回-Swing

概述 swing 是阿里原创的 i2i 召回算法&#xff0c;在阿里内部的多个业务场景被验证是一种非常有效的召回方法。据笔者了解&#xff0c;swing 在工业界已得到比较广泛的使用&#xff0c;抖音&#xff0c;小红书&#xff0c;B 站等推荐系统均使用了swing i2i。 1.传统 icf 算法…

MySql常见问题(长期更新)

基于mysql 8.0.3版本 一、忘记root密码1.1 、linux 系统下忘记密码1.2、Windows 系统下忘记密码1.3 Unix 和类 Unix 系统 二、账号问题2.1 远程访问账号设置 一、忘记root密码 1.1 、linux 系统下忘记密码 啥&#xff1f;你问我为什么会忘记密码&#xff1f;别问&#xff0c;…

Flutter状态管理新的实践 | 京东云技术团队

1 背景介绍 1.1 声明式ui 声明式UI其实并不是近几年的新技术&#xff0c;但是近几年声明式UI框架非常的火热。单说移动端&#xff0c;跨平台方案有&#xff1a;RN、Flutter。iOS原生有&#xff1a;SwiftUI。android原生有&#xff1a;compose。可以看到声明式UI是以后的前端发…