C++系列-多态的基本语法

多态的基本语法

  • 多态的含义
    • 静态多态
    • 动态多态
  • 多态的底层原理
  • 多态中的final和override
    • final
    • override:
  • 多态的应用和优点
    • 计算器简单实现
    • 电脑组装的实现


《游山西村》

南宋·陆游

莫笑农家腊酒浑,丰年留客足鸡豚。

山重水复疑无路,柳暗花明又一村。

箫鼓追随春社近,衣冠简朴古风存。

从今若许闲乘月,柱杖无时夜叩门。


多态的含义

通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态

  • C++多态性(Polymorphism)是面向对象编程(OOP)的一个重要特性之一。
  • 它允许我们使用统一的接口来处理不同类型的对象。
  • 多态性使得程序更加灵活、可扩展并且易于维护。

静态多态

  • 函数重载,运算符重载这些都属于静态多态,复用函数名。
  • 编译阶段就确定了函数的地址。

动态多态

  • 使用了一种动态绑定的技术,这个技术的核心就是虚函数表(virtual table)。
  • 运行阶段才确定函数的地址。

看一下下面这段代码,执行eat函数,想通过父类的引用指向子类的对象,用于实现子类的函数调用,可是事与愿违。
查看父类的大小,因为非静态成员函数并非属于类的对象上,所以size是1。

code:
#include<iostream>
using namespace std;class Vegetable
{
public:void eat(){cout << "多吃蔬菜对身体好哦!" << endl;}
};class Spinach :public Vegetable
{
public:void eat(){cout << "多吃 菠菜 对身体好哦!" << endl;}
};class Taro :public Vegetable
{
public:void eat(){cout << "多吃 芋头 对身体好哦!" << endl;}
};void eat(Vegetable &veg)
{veg.eat();
}int main()
{Spinach sp1;eat(sp1);Taro ta1;eat(ta1);cout << "sizeof(Vegetable) = " << sizeof(Vegetable) << endl;cout << "sizeof(Spinach) = " << sizeof(Spinach) << endl;cout << "sizeof(Taro) = " << sizeof(Taro) << endl;system("pause");return 0;
}result:
多吃蔬菜对身体好哦!
多吃蔬菜对身体好哦!
sizeof(Vegetable) = 1
sizeof(Spinach) = 1
sizeof(Taro) = 1

接着改变一个地方,在父类函数前加上virtual关键字,同时对子类中的函数进行重写。
通过父类的引用指向子类的对象,实现了子类的函数调用。
查看类的结构,大小变为4,vfptr virtual function pointer, 它指向一个vftable,和操作系统有关,有的是8。

  • 所谓的虚函数,就是被virtual修饰的类成员函数
  • 子类中有一个跟父类完全相同的虚函数(即子虚函数与父类虚函数的返回值类型、函数名字、参数列表完全相同),称子类的虚函数重写了父类的虚函数。
  • 子类在重写父类的虚函数时,在函数前可以加virtual,可以不加,建议加。
code:
#include<iostream>
using namespace std;class Vegetable
{
public:virtual void eat(){cout << "多吃蔬菜对身体好哦!" << endl;}
};class Spinach :public Vegetable
{
public:virtual void eat(){cout << "多吃 菠菜 对身体好哦!" << endl;}
};class Taro :public Vegetable
{
public:virtual void eat(){cout << "多吃 芋头 对身体好哦!" << endl;}
};void eat(Vegetable &veg)		// 父类的引用指向子类的对象
{veg.eat();
}int main()
{Spinach sp1;eat(sp1);Taro ta1;eat(ta1);cout << "sizeof(Vegetable) = " << sizeof(Vegetable) << endl;cout << "sizeof(Spinach) = " << sizeof(Spinach) << endl;cout << "sizeof(Taro) = " << sizeof(Taro) << endl;system("pause");return 0;
}result:
多吃 菠菜 对身体好哦!
多吃 芋头 对身体好哦!
sizeof(Vegetable) = 4
sizeof(Spinach) = 4
sizeof(Taro) = 4

在这里插入图片描述

在这里插入图片描述


如果子类中不重写父类中的虚函数,类的结构如下:
vftable中的函数地址父类和子类是相同的,执行的都是父类的函数。
在这里插入图片描述
在这里插入图片描述


多态的底层原理

  • 在类的结构中,有一个vfptr指向一个vftable。
  • vftable中放置了虚函数的地址,当子类中未重写该函数时,该地址是父类函数的地址,否则为子类函数的地址。
  • 如果有多个虚函数,vftable放置的是虚函数的地址对应的数组。

多态中的final和override

  • 有些情况下由于疏忽,比如函数名书写错误,导致无法实现多态,这种错误在编译期间是不会报出的,但在运行期间得不到正确的结果。
  • C++11提供了override和final两个关键字,可以帮助用户检测是否重写。

final

  • 修饰虚函数,表示该虚函数不能再被重写。
  • virtual 返回类型 函数名(形参) final {}

override:

  • 检查子虚函数是否重写了父类某个虚函数,如果没有重写编译报错。
  • virtual 返回类型 函数名(形参) override {}

多态的应用和优点

  • 代码组织结构清晰
  • 可读性强
  • 利于代码维护

计算器简单实现

//在上述代码中,我们首先定义了一个抽象基类  Operation ,其中包含一个纯虚函数  calculate 。
//然后,分别创建了加法、减法、乘法和除法的子类来实现具体的计算逻辑。
//在 main 函数中,根据用户的选择创建相应的子类对象,让父类指针指向它,并通过多态调用计算函数得到结果。
#include <iostream>
using namespace std;// 抽象父类,纯虚函数
class Operation 
{
public:virtual double calculate(double num1, double num2) = 0;     // 纯虚函数
};// 加法操作类
class Add : public Operation 
{
public:double calculate(double num1, double num2) override         // override表示是重写父类中的函数{return num1 + num2;}
};// 减法操作类
class Subtract : public Operation 
{
public:double calculate(double num1, double num2) override{return num1 - num2;}
};// 乘法操作类
class Multiply : public Operation 
{
public:double calculate(double num1, double num2) override {return num1 * num2;}
};// 除法操作类
class Divide : public Operation {
public:double calculate(double num1, double num2) override {if (num2 != 0) {return num1 / num2;}else {cout << "除数不能为 0" << endl;return 0;}}
};int main() 
{Operation* op;      // 父类指针double num1, num2, result;int choice;while (1){cout << "------------ 请选择操作 ------------" << endl;cout << "1. 加法" << endl;cout << "2. 减法" << endl;cout << "3. 乘法" << endl;cout << "4. 除法" << endl;cout << "0. 退出" << endl;cin >> choice;          // if (choice == 0){cout << "退出计算过程" << endl;break;}else{cout << "请输入第一个数:";cin >> num1;cout << "请输入第二个数:";cin >> num2;switch (choice){case 1:op = new Add();     // 父类指针指向子类对象break;case 2:op = new Subtract();break;case 3:op = new Multiply();break;case 4:op = new Divide();break;default:cout << "无效的选择" << endl;return 1;}result = op->calculate(num1, num2);     // 通过父类指针指向的是哪一个子类对象实现其对应的计算cout << "结果是:" << result << endl;delete op;}}system("pause");return 0;
}

电脑组装的实现

  • 假设电脑的主要部件为CPU,内存条,显卡, 各有不同的厂家,不同厂家的器件特性不一样,后续可能会不断增加。
  • 将每个零件封装出抽象父类,然后不同的厂家的零件定义子类,这样可以便于后续的维护和增加。
  • 定义一个计算机类,其中实现初始化,组装操作等。
    在组装过程中,打印出各零件的厂家及零件类型信息。
#include <iostream>
using namespace std;// 假设电脑的主要部件为CPU,内存条,显卡, 各有不同的厂家,不同厂家的器件特性不一样,后续可能会不断增加。
// 将每个零件封装出抽象父类,然后不同的厂家的零件定义子类,这样可以便于后续的维护和增加。
// 定义一个计算机类,其中实现初始化,组装操作等。// 每个零件定义父类,实现多态应用,定义虚函数或者纯虚函数。
class Cpu
{
public:virtual void calculate() {};
};class Memory
{
public:virtual void storage() {};
};class VideoCard
{
public:virtual void display() {};
};// 不同厂家的零件继承零件父类,并实现具体的函数,实现多态应用。
class AmdCpu: public Cpu
{
public:void calculate() {cout << "amd cpu calculate." << endl;};
};class IntelCpu : public Cpu
{
public:void calculate(){cout << "intel cpu calculate." << endl;};
};class SamsungMemory : public Memory
{
public:void storage(){cout << "samsung memory save data." << endl;};
};class MicronMemory : public Memory
{
public:void storage(){cout << "micron memory save data." << endl;};
};class AmdVideoCard : public VideoCard
{
public:void display(){cout << "amd video card display." << endl;};
};class NvidiaVideoCard : public VideoCard
{
public:void display(){cout << "nvidia video card display." << endl;};
};// 定义计算机类,实现初始化,组装操作等,因为子类对象开辟在堆区,所以重写析构函数,实现堆区内存释放
class Computer
{
public:Computer(Cpu *cpu, Memory *memory, VideoCard *video_card){m_cpu = cpu;m_memory = memory;m_video_card = video_card;}void assemble(){m_cpu->calculate();m_memory->storage();m_video_card->display();}// 释放堆区内存~Computer(){if (m_cpu != NULL){delete m_cpu;m_cpu = NULL;}if (m_memory != NULL){delete m_memory;m_memory = NULL;}if (m_video_card != NULL){delete m_video_card;m_video_card = NULL;}}
private:Cpu *m_cpu;Memory *m_memory;VideoCard *m_video_card;
};void test01()
{Cpu *intel_cpu = new IntelCpu;Memory *samsung_memory = new SamsungMemory;VideoCard* amd_video_card = new AmdVideoCard;Computer *com1 = new Computer(intel_cpu, samsung_memory, amd_video_card);com1->assemble();delete com1;
}void test02()
{Cpu* amd_cpu = new AmdCpu;Memory* micron_memory = new MicronMemory;VideoCard* amd_video_card = new AmdVideoCard;Computer* com1 = new Computer(amd_cpu, micron_memory, amd_video_card);com1->assemble();delete com1;
}int main()
{cout << "\n-------- 第 1 台电脑组装 --------" << endl;test01();cout << "\n-------- 第 2 台电脑组装 --------" << endl;test02();system("pause");return 0;
}result:
--------1 台电脑组装 --------
intel cpu calculate.
samsung memory save data.
amd video card display.--------2 台电脑组装 --------
amd cpu calculate.
micron memory save data.
amd video card display.

注意:部门内容来自黑马视频课程

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

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

相关文章

STL—list—模拟实现【迭代器的实现(重要)】【基本接口的实现】

STL—list—模拟实现 1.list源代码 要想模拟实现list&#xff0c;还是要看一下STL库中的源代码。 _list_node里面装着指向上一个节点的指针prev&#xff0c;和指向下一个节点的指针next&#xff0c;还有数据data 并且它给的是void*&#xff0c;导致后面进行节点指针的返回时…

GitHub开源的轻量级文件服务器,可docker一键部署

文件服务器 介绍安装使用命令使用API调用 介绍 项目github官网地址 Dufs是一款由Rust编写的轻量级文件服务器&#xff0c;不仅支持静态文件服务&#xff0c;还能轻松上传、下载、搜索文件&#xff0c;甚至支持WebDAV&#xff0c;让我们通过Web方式远程管理文件变得轻而易举。…

免费生产设备日志采集工具

使用咨询: 扫码添加QQ 永久免费: Gitee下载最新版本 使用说明: CSDN查看使用说明 功能: 定时(全量采集or增量采集) SCADA,MES等系统采集工控机,办公电脑文件. 优势1: 开箱即用. 解压直接运行.插件集成下载. 优势2: 批管理设备. 配置均在后台配置管理. 优势3: 无人值守 采集端…

软考-软件设计师(程序设计语言习题)

&#x1f49d;&#x1f49d;&#x1f49d;欢迎来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里可以感受到一份轻松愉快的氛围&#xff0c;不仅可以获得有趣的内容和知识&#xff0c;也可以畅所欲言、分享您的想法和见解。 非常期待和您一起在这个小…

Vue: v-html安全性问题

一、问题描述 可能都知道使用v-html插入富文本&#xff0c;存在安全隐患&#xff0c;比如 cross-site scripting attack&#xff08;xss&#xff09;。但具体什么情况下v-html会引发安全问题呢&#xff1f;是否内容中含有<scrpit>标签就会触发执行脚本呢&#xff1f; 二…

基于vue框架的北城招聘管理平台题目7lly3(程序+源码+数据库+调试部署+开发环境)系统界面在最后面。

系统程序文件列表 项目功能&#xff1a;用户,企业,企业信息,职位类型,职位信息,简历信息,职位应聘,求职意愿,面试信息,录取信息,实习信息,冻结信息,解冻信息 开题报告内容 基于Vue框架的北城招聘管理平台 开题报告 一、引言 随着互联网的飞速发展和企业对人才需求的不断增…

Redis的缓存淘汰策略

1. 查看Redis 最大的占用内存 打开redis配置文件, 设置maxmemory参数&#xff0c;maxmemory 是bytes字节类型, 注意转换 2. Redis默认内存多少可以用 注意: 在64bit系统下&#xff0c; maxmemory 设置为 0 表示不限制Redis内存使用 3. 一般生产上如何配置 一般推荐Redis 设置内…

Java中的Map(如果想知道Java中有关Map的知识点,那么只看这一篇就足够了!)

前言&#xff1a;在Java编程语言中&#xff0c;集合框架&#xff08;Collection Framework&#xff09;提供了一系列用于存储和操作数据的接口和类。其中&#xff0c;Map和Set是两个非常重要的接口&#xff0c;分别用于存储键值对和无重复元素的集合。 ✨✨✨这里是秋刀鱼不做梦…

【蓝桥杯集训100题】scratch时间计算 蓝桥杯scratch比赛专项预测编程题 集训模拟练习题第26题

目录 scratch时间计算 一、题目要求 编程实现 二、案例分析 1、角色分析 2、背景分析 3、前期准备 三、解题思路 1、思路分析 2、详细过程 四、程序编写 五、考点分析 六、推荐资料 1、入门基础 2、蓝桥杯比赛 3、考级资料 4、视频课程 5、python资料 scratc…

qtsql连接达梦数据库

odbc window和linux都有odbc的中间件&#xff0c;可以通过odbc中间件配合qtsql连接数据库 windows下配置odbc linux配置odbc apt install unixodbc unixodbc-dev /etc/odbcinst.ini配置 [DM8 ODBC DRIVER] DescriptionDM8 ODBC Driver DRIVER/opt/dmdbms/bin/libdodbc.so/et…

力扣: 两数之和 梦开始的地方

文章目录 需求暴力求解优化一下暴力解法用Map结尾 需求 给定一个整数数组 nums 和一个整数目标值 target&#xff0c;请你在该数组中找出 和为目标值 target 的那 两个 整数&#xff0c;并返回它们的数组下标。 你可以假设每种输入只会对应一个答案&#xff0c;并且你不能使用…

Leetcode刷题:哈希表

找一个数是否出现过或一个数是否在集合中的时候就要想到用哈希表法 242有效的字母异位词 bool isAnagram(string s, string t) {int table[26];for(char i:s) {table[i-a] 1;}for(char i:t) {table[i-a] -1;}for(int i:table) {if(i!0)return false;}return true;} 349两个数…

docker-harbor私有仓库部署和管理

harbor&#xff1a;开源的企业级的docker仓库软件 仓库&#xff1a;私有仓库 公有仓库 &#xff08;公司内部一般都是私有仓库&#xff09; habor 是有图形化的&#xff0c;页面UI展示的一个工具&#xff0c;操作起来很直观。 harbor每个组件都是由容器构建的&#xff0c;所…

新手教学系列——利用 Loguru 对日志进行分类处理

在现代应用程序中,日志记录是确保系统健康运行的关键因素之一。尤其在复杂的系统中,我们可能需要将日志按不同的需求进行分类和处理。Loguru 作为一款功能强大的日志库,提供了灵活的日志记录方式。今天,我们将探讨如何使用 Loguru 的过滤功能来分类处理系统日志和关键节点日…

算法-矩阵置零(73)

leetcode题目链接 这道题因为要求在O&#xff08;1&#xff09;的空间复杂度下面完成&#xff0c;所以最好的情况就是利用矩阵本身有的元素进行代码编写&#xff0c;而不另外开辟空间。 所以思路如下&#xff1a; 1.遍历第一行第一列&#xff0c;观察是否需要置0&#xff0c…

自定义注解,实现字段加密解密

根据业务需求,要求多部分字段,进行加解密,想到实现方式,就是通过自定义的注解AOP来实现 首先新建一个注解,注意ElementType.FIELD类型,说明这个注解只能作用在字段上 Target({ElementType.FIELD}) Retention(RetentionPolicy.RUNTIME) public interface NeedEncrypt { }在新建…

[CLIP-VIT-L + Qwen] 多模态大模型源码阅读 - trainer篇

[CLIP-VIT-L Qwen] 多模态大模型源码阅读 - trainer篇 前情提要源码阅读导包逐行解读compute_loss方法&#xff08;重构&#xff09;整体含义逐行解读 save_model函数&#xff08;重构&#xff09;整体含义逐行解读 create_optimizer函数&#xff08;重构&#xff09;整体含义…

CI/CD

目录 1.什么是CI/CD? 2.Gitlab仓库部署 3.部署Jenkins 3.1 使用jenkins拉取代码 3.2 对代码进行编译、打包 4.部署tomcat服务器 1.什么是CI/CD? 通俗来说就是启动一个服务&#xff0c;能够监听代码变化&#xff0c;然后自动执行打包&#xff0c;发布等流程: CICD 是持…

Jmeter版本下载国内外镜像源

官网最新版本 https://archive.apache.org/dist/jmeter/binaries/历史版本 https://archive.apache.org/dist/jmeter/binaries/ 国内镜像源1.阿里云 https://mirrors.aliyun.com/apache/jmeter/binaries/2.腾讯云 https://mirrors.cloud.tencent.com/apache/jmeter/

dubbo:dubbo+nacos整合springcloud gateway实现网关(三)

文章目录 0. 引言1. 集成gateway网关1.1 实操步骤1.2 dubbo提供者注册到nacos出现两个实例的问题 2. 源码3. 总结 0. 引言 上次我们讲到使用zookeeper作为注册中心搭建dubbo微服务框架&#xff0c;但是我们还缺少一个服务总入口&#xff0c;也就是我们的网关服务。所以今天我们…