当前位置: 首页 > news >正文

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.

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

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 【Linux —— 生产者消费者模型】
  • 47.【C语言】指针(重难点)(J)
  • 【渗透测试】ATTCK靶场一,phpmyadmin,域渗透,内网横向移动攻略
  • Unity动画模块 之 动画层混合
  • 我要做全栈:自学前端第一天
  • Go开发桌面客户端软件小试:网站Sitemap生成
  • client网络模块的开发和client与server端的部分联动调试
  • 流体力学解迷宫
  • 【ZYNQ MPSoC开发】PS裸机多核程序的固化
  • 计算机毕业设计选题推荐-体育馆场地预约系统-Java/Python项目实战
  • 学习MyBatis-Plus
  • (一) 初入MySQL 【认识和部署】
  • 【C语言从不挂科到高绩点】02-变量、数据类型、标识符、关键字
  • C的温故而知新:位操作(C Primer Plus第十五章)
  • 亲测解决electron的Unhandled Rejection
  • php的引用
  • [ JavaScript ] 数据结构与算法 —— 链表
  • Docker容器管理
  • iOS小技巧之UIImagePickerController实现头像选择
  • java8 Stream Pipelines 浅析
  • JavaScript类型识别
  • JS基础篇--通过JS生成由字母与数字组合的随机字符串
  • log4j2输出到kafka
  • Mocha测试初探
  • Mysql5.6主从复制
  • Odoo domain写法及运用
  • Promise面试题,控制异步流程
  • Twitter赢在开放,三年创造奇迹
  • VirtualBox 安装过程中出现 Running VMs found 错误的解决过程
  • Vue.js-Day01
  • 大主子表关联的性能优化方法
  • 和 || 运算
  • 开源中国专访:Chameleon原理首发,其它跨多端统一框架都是假的?
  • 来,膜拜下android roadmap,强大的执行力
  • 学习笔记:对象,原型和继承(1)
  • 要让cordova项目适配iphoneX + ios11.4,总共要几步?三步
  • 蚂蚁金服CTO程立:真正的技术革命才刚刚开始
  • #Z0458. 树的中心2
  • #控制台大学课堂点名问题_课堂随机点名
  • #知识分享#笔记#学习方法
  • (007)XHTML文档之标题——h1~h6
  • (BFS)hdoj2377-Bus Pass
  • (el-Transfer)操作(不使用 ts):Element-plus 中 Select 组件动态设置 options 值需求的解决过程
  • (windows2012共享文件夹和防火墙设置
  • (八)五种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (补)B+树一些思想
  • (动手学习深度学习)第13章 计算机视觉---微调
  • (免费领源码)python+django+mysql线上兼职平台系统83320-计算机毕业设计项目选题推荐
  • (三)Pytorch快速搭建卷积神经网络模型实现手写数字识别(代码+详细注解)
  • (十三)Flink SQL
  • (一)kafka实战——kafka源码编译启动
  • (转) ns2/nam与nam实现相关的文件
  • (转)C#调用WebService 基础
  • ******之网络***——物理***
  • .htaccess 强制https 单独排除某个目录