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

news2024/11/24 3:30:49

多态的基本语法

  • 多态的含义
    • 静态多态
    • 动态多态
  • 多态的底层原理
  • 多态中的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.coloradmin.cn/o/2059092.html

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

相关文章

leetcode118. 杨辉三角,老题又做

leetcode118. 杨辉三角 给定一个非负整数 numRows&#xff0c;生成「杨辉三角」的前 numRows 行。 在「杨辉三角」中&#xff0c;每个数是它左上方和右上方的数的和。 示例 1: 输入: numRows 5 输出: [[1],[1,1],[1,2,1],[1,3,3,1],[1,4,6,4,1]] 示例 2: 输入: numRows 1…

AI视频创作应用

重磅推荐专栏: 《大模型AIGC》 《课程大纲》 《知识星球》 本专栏致力于探索和讨论当今最前沿的技术趋势和应用领域,包括但不限于ChatGPT和Stable Diffusion等。我们将深入研究大型模型的开发和应用,以及与之相关的人工智能生成内容(AIGC)技术。通过深入的技术解析和实践经…

appium下载及安装

下载地址&#xff1a;https://github.com/appium/appium-desktop/releases 双击安装就可以

信号的变换

信号的变换 在实践中&#xff0c;缩放和时间平移是遇到的两个最重要的信号变换。缩放改变了振幅轴上的因变量的值&#xff0c;而时间平移则影响了时间轴上的自变量的值。 加法 对于两个离散时间信号的加法&#xff0c;例如 x [ n ] x[n] x[n] 和 y [ n ] y[n] y[n]&#x…

Flutter【02】mobx原理

简介&#xff1a; 概念 MobX 区分了以下几个应用中的概念&#xff1a; State(状态) 状态 是驱动应用的数据。 通常有像待办事项列表这样的领域特定状态&#xff0c;还有像当前已选元素的视图状态。 记住&#xff0c;状态就像是有数据的excel表格。 Derivations(衍生) 任何…

Ps:首选项 - 性能

Ps菜单&#xff1a;编辑/首选项 Edit/Preferences 快捷键&#xff1a;Ctrl K Photoshop 首选项中的“性能” Performance选项卡允许用户通过调整内存使用、GPU 设置、高速缓存设置以及多线程处理等选项&#xff0c;来优化 Photoshop 的性能。这对于处理大文件、复杂图像或需要…

Python 数据分析之Numpy学习(一)

Python 数据分析之Numpy学习&#xff08;一&#xff09; 一、Numpy的引入 1.1 矩阵/向量的按位运算 需求&#xff1a;矩阵的按位相加 [0,1,4] [0,1,8] [0,2,12] 1.1.1 利用python实现矩阵/向量的按位运算 # 1.通过列表实现 list1 [0, 1, 4] list2 [0, 1, 8]# 列表使用…

(17)ELK大型储存库的搭建

前言&#xff1a; els是大型数据储存体系&#xff0c;类似于一种分片式存储方式。elasticsearch有强大的查询功能&#xff0c;基于java开发的工具&#xff0c;结合logstash收集工具&#xff0c;收集数据。kibana图形化展示数据&#xff0c;可以很好在大量的消息中准确的找到符…

Marimo:下一代Python编程环境,颠覆传统Jupyter笔记本,自动化执行所有依赖代码块,告别繁琐手动操作

Marimo 是一个颠覆传统笔记本的全新编程环境&#xff0c;它以其反应式、交互式、可执行和可共享等特性&#xff0c;为开发者们带来前所未有的编程体验。Marimo 确保您的笔记本代码、输出和程序状态始终保持一致。它解决了传统笔记本&#xff08;如 Jupyter&#xff09;的许多问…

流媒体服务器如何让WebRTC支持H.265,同时又能支持Web js硬解码、软解码(MSE硬解、WASM软解)

为了这一整套的解决方案&#xff0c;调研研发整整花费了差不多半年多的时间&#xff0c;需达成的目标&#xff1a; 流媒体服务器端不需要将H.265转码成H.264&#xff0c;就能让Chrome解码播放H.265&#xff1b; 注意&#xff1a;现在很多市面上的软硬件通过转码H.265成H.264的…

CSP-CCF 202312-1 仓库规划

一、问题描述 二、解答 思路&#xff1a;定义二维数组&#xff0c;比较不同行的相同列数 代码如下&#xff1a; #include<iostream> using namespace std; int main() {int n, m;cin >> n >> m;int a[1001][11] { 0 };for (int i 1; i < n; i){for (…

贪心 + 分层图bfs,newcoder 76652/B

目录 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 二、解题报告 1、思路分析 2、复杂度 3、代码详解 一、题目 1、题目描述 2、输入输出 2.1输入 2.2输出 3、原题链接 https://ac.nowcoder.com/acm/contest/76652/B 二、解题报告 1、思路分析…

ELK基础搭建

一、认识ELK ELK是一套开源的日志分析系统&#xff0c;由elasticsearchlogstashKibana组成。 官网说明&#xff1a;https://www.elastic.co/cn/products 首先: 先一句话简单了解 E&#xff0c;L&#xff0c;K 这三个软件 elasticsearch: 分布式搜索引擎 logstash: 日志收集与…

领英(LinkedIn)公司主页创建方法分享

上次写了几篇关于领英注册的文章&#xff0c;也是有不少人加我&#xff0c;说有用。当然了也有还是不行的&#xff0c;还是不行的话一般都是比较复杂的问题&#xff0c;需要一些技术性的手段去解决。 然后最近也是有一些外贸朋友问公司主页注册创建的一些事情&#xff0c;今天的…

指挥调度平台——数字赋能,让出行更有温度

智慧交通指挥调度平台是基于信息技术和智能化系统的创新解决方案&#xff0c;旨在提升城市交通管理效率、改善交通流畅度、减少拥堵问题&#xff0c;以及增强城市交通运行的智能化水平。该平台整合了大数据分析、实时监测、智能优化算法等技术&#xff0c;为交通管理部门提供全…

虚拟现实技术的发展现状如何?

虚拟现实&#xff08;VR&#xff09;技术自2016年被广泛认为是元年之后&#xff0c;经历了快速增长和随后的调整期。目前&#xff0c;VR行业正处于快速发展期&#xff0c;技术不断进步&#xff0c;应用场景持续拓展。2024年VR技术发展现状概述&#xff1a; 1、行业发展阶段&am…

独家揭秘丨GreatSQL 的MDL锁策略升级对执行的影响

独家揭秘丨GreatSQL 的MDL锁策略升级对执行的影响 一、MDL锁策略介绍 GreatSQL 的MDL锁有个策略方法类MDL_lock_strategy&#xff0c;它根据对象的类型分为了scope类型和object类型&#xff0c;前者主要用于GLOBAL, COMMIT, TABLESPACE, BACKUP_LOCK and SCHEMA ,RESOURCE_GR…

基于tinymce实现多人在线实时协同文本编辑

基于tinymce实现多人在线实时协同文本编辑 前言 这可能是最后一次写tinymce相关的文章了&#xff0c;一方面tinymce的底层设计限制了很多功能的实现&#xff0c;另一方面tinymce本身越来越商业化&#xff0c;最新的7版本已经必须配置key&#xff0c;否则面临无法使用的问题。…

PPT如何添加水印?推荐两种方法!

在PPT演示文稿中添加水印&#xff0c;可以有效地保护版权或在背景上增加品牌标识。本文将介绍两种在PPT中添加水印的方法&#xff0c;帮助你轻松实现这一功能&#xff0c;一起来看看吧&#xff01; 方法一&#xff1a;在单张幻灯片上添加水印 1、选择目标幻灯片 打开PPT文件&…

防近视台灯有效果吗?学生家长们应该了解护眼台灯怎么选

在当前社会&#xff0c;近视的影响不容小视&#xff0c;除了对视觉健康的影响外&#xff0c;近视还可能对个人的心理健康产生负面影响。视力不佳可能导致自卑感和社会交往障碍&#xff0c;尤其是在儿童和青少年时期。保护视力健康要从小做起&#xff0c;家长们可以关注孩子的用…