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;
}
相关文章:
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面向对象三大特性之一 多态分为两类 静志多态: 函数…...

QT Udp广播实现设备发现
测试环境 本文选用pc1作为客户端,pc2,以及一台虚拟机作为服务端。 pc1,pc2(客户端): 虚拟机(服务端): 客户端 原理:客户端通过发送广播消息信息到ip:255.255.255.255(QHostAddress::Broadcast),局域网…...
PyTorch 统计属性-Tensor基本操作
最小 min, 最大 max, 均值 mean,累加 sum,累乘 prod … >>> a torch.arange(0,8).view(2,4).float() >>> a tensor([[0., 1., 2., 3.],[4., 5., 6., 7.]])>>> a.min() ## 最小值:tensor(0.) >>> a.ma…...

波拉西亚战记加速器 台服波拉西亚战记免费加速器
波拉西亚战记是一款新上线的MMORPG游戏,游戏内我们有多个角色职业可以选择,可以体验不同的战斗流派玩法,开放式的地图设计,玩家可以自由的进行探索冒险,寻找各种物资。各种随机事件可以触发,让玩家的冒险过…...
Mocha + Chai 测试环境配置,支持 ES6 语法
下面是一个完整的 Mocha Chai 测试环境配置,支持 ES6 语法。我们将使用 Babel 来转译 ES6 代码。 步骤一:初始化项目 首先,在项目目录中运行以下命令来初始化一个新的 Node.js 项目: npm init -y步骤二:安装必要的…...
华为网络设备攻击防范
畸形报文攻击防范 攻击行为 畸形报文攻击是通过向交换机发送有缺陷的IP报文,使得交换机在处理这样的IP包时会出现崩溃,给交换机带来损失。 畸形报文攻击主要有如下几种: 没有IP载荷的泛洪攻击 IGMP空报文攻击 LAND攻击 Smurf攻击 TCP标…...
RK3588开发笔记-100M网口自协商成1000M网口
目录 前言 一、问题描述 二、原理图连接 三、解决方法 总结 前言 在进行RK3588开发过程中,遇到一个令人困惑的问题:在使用RTL8211F-CG phy芯片出来的100M网口在自协商后连接速率变成了1000M。这篇博客将详细记录这个问题的产生、排查过程以及最终的解决方案,希望能对遇到…...

Python第二语言(十三、PySpark实战)
目录 1.开篇 2. PySpark介绍 3. PySpark基础准备 3.1 PySpark安装 3.2 掌握PySpark执行环境入口对象的构建 3.3 理解PySpark的编程模型 4. PySpark:RDD对象数据输入 4.1 RDD对象概念:PySpark支持多种数据的输入,完成后会返回RDD类的对…...
《阅读的方法》读后感——超越期待的收获
当我翻开这本书的扉页时,未曾料到它会给我带来如此深远的启示和收获。依照推荐序言中的指引,我随意翻阅、精心选读,每一次都如同打开一扇新的窗户,让我窥见不同领域的智慧和美好。 等地铁时、临睡前随便读点什么,有什么…...
算法训练营第五十八天 | LeetCode 392 判断子序列、卡码网模拟美团笔试第一、二、三题(300/500有待提高)
卡码网图论更新了可以去看看,模拟笔试第四题就是深搜/广搜还不太会 LeetCode 392 判断子序列 其实就是最长公共子序列翻版 代码如下: class Solution {public boolean isSubsequence(String s, String t) {int[][] dp new int[s.length() 1][t.lengt…...

Sa-Token鉴权与网关服务实现
纠错: 在上一部分里我完成了微服务框架的初步实现,但是先说一下之前有一个错误,就是依赖部分 上次的学习中我在总的父模块下引入了spring-boot-dependencies(版本控制)我以为在子模块下就不需要再引用了,…...

企事业单位安全生产月活动怎样向媒体投稿?
作为一名单位的信息宣传员,我肩负着将每一次重要活动的精彩瞬间转化为文字,向外界传递我们单位声音的重任。初入此行时,我满怀热情,坚信通过传统的方式——电子邮件投稿,能够有效地将我们的故事传播出去。然而,现实却给我上了生动的一课。 记得在筹备“安全生产月”活动的宣传时…...
MySQL8.0默认TCP端口介绍
1、本文内容 选择题TCP/IPMySQL 8.0 的默认TCP端口show variables查看总结 2、选择题 A、3306 B、33060 C、33062 D、33063 3、TCP/IP TCP/IP(Transmission Control Protocol/Internet Protocol,传输控制协议/网际协议)是指能够在多个不同…...

Javaweb避坑指北(持续更新)
内容较多可按CtrlF搜索 0.目录 1.获取插入数据后自增长主键的值 2.Controller中返回给ajax请求字符串/json会跳转到xxx.jsp 3.ajax请求获得的json无法解析 4.在Controller中使用ServletFileUpload获取的上传文件为null 5.莫名其妙报service和dao里方法的错误 6.ajax请求拿…...
Web前端知道:深入探索与无尽挑战
Web前端知道:深入探索与无尽挑战 Web前端,这个看似简单却实则深不可测的领域,一直以来都吸引着无数开发者投入其中。在这个充满未知与可能的世界里,我们既是探索者,也是挑战者。本文将从四个方面、五个方面、六个方面…...

QT调用vs2019生成的c++动态库
QT调用vs2019生成的c动态库 dll库的创建方法: VS2019创建c动态链接库dll与调用方法-CSDN博客 加减法示范: 头文件 // 下列 ifdef 块是创建使从 DLL 导出更简单的 // 宏的标准方法。此 DLL 中的所有文件都是用命令行上定义的 DLL3_EXPORTS // 符号编…...
C语言TC中有⼏个画线函数?怎么使⽤?
一、问题 C语⾔中画线的函数好像不⽌ line( )⼀个,那么除了 line( ) ,还有哪些画线函数?怎么使⽤? 二、解答 TC中有3种画线的函数,共语法格式如下。 void far line(int x0, int y0, int xl, int y1); void far linet…...

掌握WhoisAPI,提升域名管理的效率
在互联网时代,域名管理是网站运营中非常重要的一环。通过域名,我们能够轻松访问和识别不同的网站。然而,域名的注册和管理也是一项复杂的任务,特别是对于大规模拥有许多域名的企业来说。为了提升域名管理的效率,我们可…...

Docker与Docker-Compose详解
1、Docker是什么? 在计算机中,虚拟化(英语: Virtualization) 是一种资源管理技术,是将计算机的各种实体资源,如服务器、网络、内存及存储等,予以抽象、转换后呈现出来,打破实体结构间的不可切割的障碍&…...

微服务之熔断器
1、高并发带来的问题 在微服务架构中,我们将业务拆分成一个个的服务,服务与服务之间可以相互调用,但是由于网络原因 或者自身的原因,服务并不能保证服务的100%可用,如果单个服务出现问题,调用这个服务就会…...

微信小程序之bind和catch
这两个呢,都是绑定事件用的,具体使用有些小区别。 官方文档: 事件冒泡处理不同 bind:绑定的事件会向上冒泡,即触发当前组件的事件后,还会继续触发父组件的相同事件。例如,有一个子视图绑定了b…...

大话软工笔记—需求分析概述
需求分析,就是要对需求调研收集到的资料信息逐个地进行拆分、研究,从大量的不确定“需求”中确定出哪些需求最终要转换为确定的“功能需求”。 需求分析的作用非常重要,后续设计的依据主要来自于需求分析的成果,包括: 项目的目的…...
论文解读:交大港大上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一)
宇树机器人多姿态起立控制强化学习框架论文解析 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化学习框架(一) 论文解读:交大&港大&上海AI Lab开源论文 | 宇树机器人多姿态起立控制强化…...
css的定位(position)详解:相对定位 绝对定位 固定定位
在 CSS 中,元素的定位通过 position 属性控制,共有 5 种定位模式:static(静态定位)、relative(相对定位)、absolute(绝对定位)、fixed(固定定位)和…...

BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...
大模型多显卡多服务器并行计算方法与实践指南
一、分布式训练概述 大规模语言模型的训练通常需要分布式计算技术,以解决单机资源不足的问题。分布式训练主要分为两种模式: 数据并行:将数据分片到不同设备,每个设备拥有完整的模型副本 模型并行:将模型分割到不同设备,每个设备处理部分模型计算 现代大模型训练通常结合…...

初学 pytest 记录
安装 pip install pytest用例可以是函数也可以是类中的方法 def test_func():print()class TestAdd: # def __init__(self): 在 pytest 中不可以使用__init__方法 # self.cc 12345 pytest.mark.api def test_str(self):res add(1, 2)assert res 12def test_int(self):r…...

保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...

基于 TAPD 进行项目管理
起因 自己写了个小工具,仓库用的Github。之前在用markdown进行需求管理,现在随着功能的增加,感觉有点难以管理了,所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD,需要提供一个企业名新建一个项目&#…...
【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权
摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题:安全。文章将详细阐述认证(Authentication) 与授权(Authorization的核心概念,对比传统 Session-Cookie 与现代 JWT(JS…...