C++学习笔记“类和对象”:多态;

目录

4.7 多态

4.7.1 多态的基本概念

 4.7.2 多态案例--计算器类

4.7.3 纯虚函数和抽象类 

4.7.4 多态案例二 - 制作饮品

4.7.5 虚析构和纯虚析构

4.7.6 多态案例三-电脑组装 


4.7 多态

4.7.1 多态的基本概念

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

多态分为两类

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

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

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

下面通过案例进行讲解多态

#include <iostream>
using namespace std;// 多态的基本概念// 动物类
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. 父类有一个虚函数
// 3. 子类重写了父类的虚函数// 动态多态使用
// 父类的指针或引用指向子类的对象,调用父类的函数,实际调用的是子类的函数//执行说话的函数
void speak(Animal& animal) {// 这里的Animal&参数表示传入的是Animal类的引用animal.speak();// 调用Animal类的speak()函数
}void test() {Cat cat;// 实例化一个猫对象Dog dog;// 实例化一个狗对象speak(cat); // 输出 "猫说话"speak(dog); // 输出 "狗说话"
}int main() {test();return 0;
}

总结:
多态满足条件

  • 有继承关系
  • 子类重写父类中的虚函数

多态便用条件

  • 父类指针或引用指向子类对象

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

 4.7.2 多态案例--计算器类

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

  • 代码组织结构清晰
  • 可读性强
  • 利于前期和后期的扩展以及维护

示例:

#include <iostream>
using namespace std;// 多态案例--计算器类
// 案例描述:设计一个计算器类,分别利用普通写法和多态技术,设计实现两个操作数进行运算的计算器类// 普通写法
class Calculator {
public:int getResult(string oper) {if (oper == "+") {return num_1 + num_2;}else if (oper == "-") {return num_1 - num_2;}else if (oper == "*") {return num_1 * num_2;}else if (oper == "/") {return num_1 / num_2;}else {cout << "Invalid operator!" << endl;return 0;}}void setNum1(int num) {num_1 = num;}void setNum2(int num) {num_2 = num;}int num_1;int num_2;
};void testCalculator() {Calculator c;c.setNum1(10);c.setNum2(5);cout << "10 + 5 = " << c.getResult("+") << endl;cout << "10 - 5 = " << c.getResult("-") << endl;cout << "10 * 5 = " << c.getResult("*") << endl;cout << "10 / 5 = " << c.getResult("/") << endl;cout << "10 % 5 = " << c.getResult("%") << endl;cout << "------------------------------------- "  << endl;
}// 利用多态实现计算器
// 多态好处
// 1、组织代码更加清晰
// 2、可读性更好
// 3、可扩展性更强,维护性更好// 定义计算器基类
class CalculatorBase {
public:virtual int getResult(){ return 0; }int num_1;int num_2;
};// 定义加法类
class Add : public CalculatorBase {
public:int getResult() {return num_1 + num_2;}
};// 定义减法类
class Subtract : public CalculatorBase {
public:int getResult() {return num_1 - num_2;}
};// 定义乘法类
class Multiply : public CalculatorBase {
public:int getResult() {return num_1 * num_2;}
};// 定义除法类
class Divide : public CalculatorBase {
public:int getResult() {return num_1 / num_2;}
};// 测试多态计算器
void testCalculator_2() {CalculatorBase *c1 = new Add();c1->num_1 = 10;c1->num_2 = 5;cout << "10 + 5 = " << c1->getResult() << endl;CalculatorBase *c2 = new Subtract();c2->num_1 = 10;c2->num_2 = 5;cout << "10 - 5 = " << c2->getResult() << endl;CalculatorBase *c3 = new Multiply();c3->num_1 = 10;c3->num_2 = 5;cout << "10 * 5 = " << c3->getResult() << endl;CalculatorBase *c4 = new Divide();c4->num_1 = 10;c4->num_2 = 5;cout << "10 / 5 = " << c4->getResult() << endl;delete c1;delete c2;delete c3;delete c4;
}// 程序入口
int main() {testCalculator();testCalculator_2();return 0;
}

 总结:C++开发提倡利用多态设计程序架构,因为多态优点很多

4.7.3 纯虚函数和抽象类 

 在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容
因此可以将虚函数改为纯虚函数
纯虚函数语法: virtual 返回值类型 函数名(参数列表)=0;
当类中有了纯虚函数,这个类也称为抽象类
抽象类特点:

  • 无法实例化对象
  • 子类必须重写抽象类中的纯虚函数,否则也属于抽象类

示例:

#include <iostream>
using namespace std;// 纯虚函数和抽象类class Base {
public:// 只要有一个纯虚函数,该类就是抽象类// 不能创建对象,只能作为基类被继承// 抽象类特点// 1. 无法实例化对象// 2. 抽象类的子类,必须要重写父类的纯虚函数,否则子类也是抽象类virtual void fun() = 0; // 纯虚函数
};class Derived : public Base {
public:virtual void fun() {cout << "Derived::fun()" << endl;}
};void test() {//Base b; // 抽象类不能创建对象// Base b1; // 抽象类不能实例化// Base b2 = new Base; // 抽象类不能实例化Derived d;// 继承抽象类,必须重写父类的纯虚函数d.fun();Base *base = new Derived; // 指针指向抽象类的子类对象,可以调用子类的成员函数base->fun(); // 调用子类的成员函数
}int main() {test();return 0;
}
4.7.4 多态案例二 - 制作饮品

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

#include <iostream>
#include <string>
using namespace std;// 制作饮品的基类
class Drink {
public:virtual void BoilWater() = 0;// 烧水virtual void AddCondiments() = 0;// 添加调料virtual void PourInCup() = 0;// 把饮品倒进杯子virtual void AddSugar() = 0;// 添加糖virtual void AddMilk() = 0;// 添加牛奶void makeDrink(){BoilWater();AddCondiments();PourInCup();AddSugar();AddMilk();}
};// 制作咖啡的子类
class Coffee : public Drink {
public:void BoilWater() {cout << "烧水" << endl;}void AddCondiments() {cout << "添加糖" << endl;}void PourInCup() {cout << "倒进杯子" << endl;}void AddSugar() {cout << "添加糖" << endl;}void AddMilk() {cout << "添加牛奶" << endl;}
};// 制作茶的子类
class Tea : public Drink {
public:void BoilWater() {cout << "烧水" << endl;}void AddCondiments() {cout << "添加柠檬" << endl;}void PourInCup() {cout << "倒进杯子" << endl;}void AddSugar() {cout << "添加糖" << endl;}void AddMilk() {cout << "添加牛奶" << endl;}
};// 制作饮品的函数
void makeDrinkWork(Drink* drink) {drink->makeDrink();// 调用基类的makeDrink()函数cout << "制作--完成" << endl;delete drink;// 释放内存
}void test() {// 制作咖啡makeDrinkWork(new Coffee());// 动态创建咖啡对象并调用makeDrinkWork()函数cout << "--------------------------------" << endl;// 制作茶makeDrinkWork(new Tea());
}int main() {test();return 0;
}
4.7.5 虚析构和纯虚析构

多态使用时,如果子类中有属性开辟到堆区,那么父类指针在释放时无法调用到子类的析构代码
解决方式:将父类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性:

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

虚析构和纯虚析构区别:

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

 虚析构语法:
virtual ~类名(){}
纯虚析构语法:
virtual~类名()=0;
类名::~类名(){}

#include <iostream>
#include <string>
using namespace std;// 虚析构和纯虚析构class Animal {
public:virtual void speak() = 0; // 纯虚函数// 利用虚析构函数释放内存,可以解决父类指针释放子类对象时不干净的问题//virtual ~Animal() {} // 虚析构函数,释放内存// 纯虚析构函数,需要声明也需要实现// 有了纯虚析构函数,这个类也就成为了抽象类,不能实例化对象,只能作为基类被派生virtual ~Animal() = 0; // 纯虚析构函数,声明,但不定义,用于派生类中
};// 纯虚析构函数,用于派生类中,声明但不定义
Animal::~Animal(){}class Cat : public Animal {
public:Cat(string *name) {m_Name = new string(*name);}virtual void speak() {cout << "Cat: " << *m_Name << ";Meow!" << endl;}~Cat() { cout << "Cat: " << *m_Name << ";Bye!" << endl; delete m_Name; } // 虚析构函数,释放内存string *m_Name;
};void test_Cat() {string name = "Kitty";// 注意:这里传递的是指针,而不是字符串Animal* a = new Cat(&name);// 注意:这里传递的是指针,而不是对象a->speak();// 注意:这里调用的是虚函数,而不是对象指针delete a;// 注意:这里释放的是指针,而不是对象
}int main() {test_Cat();return 0;
}

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

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

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

4.7.6 多态案例三-电脑组装 

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

将每个零件封装出抽象其类,并且提供不同的厂商生产不同的零件,例如Intel厂商和Lenovo厂商创建电脑类提供让电脑工作的函数,并且调用每个零件工作的接口

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

#include <iostream>
#include <string>
using namespace std;// CPU类
class CPU {
public:virtual void calculate() = 0;// 虚函数,用于计算功能
};// 主板类
class Mainboard {
public:virtual void motherboard() = 0;// 虚函数,用于组装主板
};// 内存条类
class Memory {
public:virtual void RAM() = 0;// 虚函数,用于安装内存条
};// 显卡类
class GraphicsCard {
public:virtual void display() = 0;// 虚函数,用于安装显卡
};// 电脑类
class Computer {
public:Computer(CPU* cpu, Mainboard* mainboard, Memory* memory, GraphicsCard* graphicsCard): cpu(cpu), mainboard(mainboard), memory(memory), graphicsCard(graphicsCard) {// 构造函数,传入各个部件对象/*cpu = cpu;mainboard = mainboard;memory = memory;graphicsCard = graphicsCard;*/}void assemble() {// 组装电脑cpu->calculate();mainboard->motherboard();memory->RAM();graphicsCard->display();}private:CPU* cpu;// CPU对象Mainboard* mainboard;// 主板对象Memory* memory;// 内存条对象GraphicsCard* graphicsCard;// 显卡对象
};// 具体实现类
class IntelCPU : public CPU {
public:virtual void calculate() {cout << "Intel CPU is calculating..." << endl;}
};class ASUSMainboard : public Mainboard {
public:virtual void motherboard() {cout << "ASUS Mainboard is assembling..." << endl;}
};class KingstonMemory : public Memory {
public:virtual void RAM() {cout << "Kingston Memory is installing..." << endl;}
};class NvidiaGraphicsCard : public GraphicsCard {
public:virtual void display() {cout << "Nvidia GraphicsCard is installing..." << endl;}
};void test() {// 第一台电脑零件CPU* intelCPU = new IntelCPU();Mainboard* asusMainboard = new ASUSMainboard();Memory* kingstonMemory = new KingstonMemory();GraphicsCard* nvidiaGraphicsCard = new NvidiaGraphicsCard();// 组装电脑Computer* computer = new Computer(intelCPU, asusMainboard, kingstonMemory, nvidiaGraphicsCard);computer->assemble();// 释放内存delete intelCPU;delete asusMainboard;delete kingstonMemory;delete nvidiaGraphicsCard;delete computer;
}int main() {test();return 0;
}

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

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

相关文章

apex触发器满足条件时弹出对话框

在Salesforce中&#xff0c;Apex触发器是在记录的数据库操作&#xff08;如插入、更新、删除&#xff09;之前或之后执行的逻辑。由于Apex触发器运行在服务器端&#xff0c;无法直接触发客户端&#xff08;浏览器&#xff09;上的对话框。不过可以通过以下方法间接实现这一需求…

[ue5]建模场景学习笔记(6)——必修内容可交互的地形,交互沙(4)

1.需求分析&#xff1a; 现在我们已经有了可以在世界内近于无限的跑动痕迹&#xff0c;现在需要对痕迹进行细化&#xff0c;包括例如当人物跳起时便不再绘制痕迹&#xff0c;以及痕迹应该存在深浅&#xff0c;应该由两只脚分别绘制&#xff0c;同时也应该对地面材质进行进一步处…

程序性能分析:工具与策略

在软件开发中&#xff0c;程序性能分析是一个至关重要的环节。无论是为了确保代码的正确性&#xff0c;还是为了提高程序的运行效率&#xff0c;性能分析都是不可或缺的。本文将介绍程序性能分析的策略以及一系列性能分析和内存检查工具。 一、性能分析的策略 先保证正确性&a…

Vue基本使用-02

上节我们讲了什么是mvvm模型&#xff0c;以及我们vue的一些常用指令&#xff0c;今天给大家讲一下vue的基本使用&#xff0c;在将之前我们需要重点讲解我们的一个指令&#xff0c;v-model指令 v-model v-model 可以在组件上使用以实现双向绑定,什么是双向绑定呢?意思就是当我们…

景芯SoC A72的时钟树分析

innovus的ctslog中的Clock DAG信息可以报出来CTS主要运行步骤的关键信息&#xff0c;比如clustering&#xff0c;balancing做完后的clock tree的长度&#xff0c;clock tree上所用的buffer、inverter&#xff0c;icg cell数量&#xff0c;clock skew等信息。我们以景芯SoC A72 …

wordpress站群搭建2代码初始化

海鸥技术下午茶-wordpress站群搭建2项目代码初始化 1.后端环境 项目框架使用 go-zore https://go-zero.dev/docs/tasks 集成了各种工程实践的 web 和 rpc 框架。含极简的 API 定义和生成工具 goctl&#xff0c;可以根据定义的 api 文件一键生成 Go。可以很大程度上提高开发效…

代码随想录算法训练营第三十六天|LeetCode56 合并区间、LeetCode738 单调递增的数字

题1&#xff1a; 指路&#xff1a;56. 合并区间 - 力扣&#xff08;LeetCode&#xff09; 思路与代码&#xff1a; 相似于昨天的引爆气球(第三题)&#xff0c;这个题的题意更为清晰。我们只要取有交集元素的并集区间即可&#xff0c;其他区间照常返回。同样&#xff0c;我们…

React Router 路由详解

文章目录 1. 安装 react-router-dom2. 创建路由3. 创建链接和导航4. 渲染路由参数和查询字符串 在React中&#xff0c;我们通常使用 react-router-dom 这个库来处理路由。 1. 安装 react-router-dom 首先&#xff0c;你需要安装 react-router-dom &#xff0c;可以使用 npm 或…

Centos离线安装Python3

目录 1.准备工作 2.解压python压缩包 3.编译 4.安装、更改环境变量 5.建立pip连接 使用的是Centos7服务器&#xff0c;Py版本是py3.9.0 1.准备工作 首先确保服务器中存在相关的编译器&#xff0c;例如GCC&#xff1b;这里不做过多叙述&#xff0c;需要者前往&#xff1a…

自动化压测工具开发(MFC)

1. 背景 为了减轻测试人员在进行MFC程序压力测试时的重复手动操作&#xff0c;本文档描述了开发一个自动化压力测试工具的过程。该工具能够根据程序界面某块区域的预定状态变化&#xff0c;自动执行鼠标点击或键盘输入操作。 2. 技术概览 串口控制&#xff1a;用于控制外部设…

空间搜索geohash概述;redis的geo命令

概述 通常在一些2C业务场景中会根据用户的位置来搜索一些内容。通常提供位置搜索的都是直接通过redis/mongodb/es等中间件实现的。 但是这些中间件又是怎么实现位置搜索的呢&#xff1b; 查了一番资料&#xff0c;发现背后一个公共的算法Geohash。 搜索的时候可以根据距离对…

Vitis HLS 学习笔记--移除内存分配malloc

目录 1. 简介 2. 示例解析 2.1 源码解释 2.2 malloc 分析 2.3 替代方案分析 3. 总结 1. 简介 Vitis HLS 也不支持动态创建或删除 C/C 对象&#xff08;用于综合&#xff09;。 本文探究如何在C/C代码中避免使用显式的malloc函数来分配内存。在硬件设计和FPGA开发中&…

Xcode无法使用设备:Failed to prepare the device for development

问题&#xff1a; Xcode无法使用设备开发&#xff0c;失败报错如下&#xff1a; Failed to prepare the device for development. This operation can fail if the version of the OS on the device is incompatible with the installed version of Xcode. You may also need…

致 粉丝de信

致 粉丝 -本文呢看不下去别看&#xff0c;但是学业是真的重要&#xff08;平常有信奥&#x1f62b;&#xff09;&#xff0c;电脑没收……更新可能得到暑假&#xff0c; 同学&#xff1a;小没苯agoe &#xff08;aaa&#xff0c;学霸&#xff01;&#xff01;&#xff01;&…

GGML简单介绍

GGML是一个用于机器学习的张量库&#xff0c;可以在商用硬件上实现大型模型和高性能。它被llama.cpp和whisper.cpp使用 C语言编写 16位浮点支撑 整数量化支持(如4位、5位、8位) 自动分化 内置优化算法(如ADAM, L-BFGS) 针对苹果芯片进行优化 在x86架构上利用AVX / AVX2的内在特…

A股上市公司MSCI ESG评级面板数据(2017-2023)

数据简介&#xff1a;MSCI ESG&#xff08;Environmental, Social, and Governance&#xff09;评级是由 MSCI Inc. 提供的一项服务&#xff0c;旨在评估公司在环境、社会和治理方面的表现。MSCI 是一家全球领先的投资研究和指数提供商&#xff0c;其 ESG 评级被广泛用于评估企…

C++ Primer 第五版 第16章 模板与泛型编程

模板是C中泛型编程的基础。一个模板就是一个创建类或函数的蓝图或者说公式。当使用一个vector这样的泛型类型&#xff0c;或者find这样的泛型函数时&#xff0c;我们提供足够的信息&#xff0c;将蓝图转换为特定的类或函数。这种转换发生在编译时。 一、定义模板 1. 函数模板…

windows11 建立批处理bat文件来删除指定目录下的所有隐藏的文件。

今天在导入项目的时候发现之前项目中的文件夹中有很多隐藏的临时文件&#xff0c;这个文件应该是版本控制产生的&#xff0c;导致导入后文件夹上有X&#xff0c;然后里面文件是一个没有错。 我们来建立一个bat来&#xff0c;进行批量删除隐藏文件就可以了&#xff1a; echo o…

安装前端与Web前端:深度探索与实践

安装前端与Web前端&#xff1a;深度探索与实践 在数字化时代&#xff0c;前端技术作为用户界面的直接呈现者&#xff0c;其重要性不言而喻。然而&#xff0c;对于初学者而言&#xff0c;安装前端与Web前端的过程往往充满了困惑与挑战。本文将从四个方面、五个方面、六个方面和…

[12] 使用 CUDA 进行图像处理

使用 CUDA 进行图像处理 当下生活在高清摄像头的时代,这种摄像头能捕获高达1920*1920像素的高解析度画幅。想要实施的处理这么多的数据,往往需要几个TFlops地浮点处理性能,这些要求CPU也无法满足通过在代码中使用CUDA,可以利用GPU提供的强大地计算能力CUDA支持多维地Grid和…