从0开始C++(八):多态的实现

news2024/10/7 5:45:42

 相关文章:

从0开始C++(一):从C到C++

从0开始C++(二):类、对象、封装

从0开始C++(三):构造函数与析构函数详解

从0开始C++(四):作用域限定符、this指针、static与const关键字

从0开始C++(五):友元函数&运算符重载

从0开始C++(六):模板与容器的使用

从0开始C++(七):继承

目录

什么是多态

函数覆盖

 虚函数

多态的实现

多态的原理 

 虚析构函数


什么是多态

多态是面向对象编程中的一个重要概念,它指的是同一种行为或方法可以根据不同的对象来表现出不同的形态或结果。简单来说,多态就是同一个方法在不同的对象上产生不同的效果

在面向对象编程中,多态是通过继承和方法重写来实现的。当一个父类有多个子类时,可以使用父类的引用来指向任意一个子类的对象,然后通过调用相同的方法来实现不同的行为。这样可以极大地提高代码的灵活性和可扩展性

多态的使用可以提高代码的可读性和可维护性,简化代码的逻辑结构,使代码更加灵活和易于扩展。它是面向对象编程的重要特性之一,也是面向对象编程的核心思想之一。

在面向对象编程中,我们通常将多态分为两种类型:静态多态(也被称为编译时多态)、动态多态(也被称为运行时多态)。

静态多态:

● 静态多态是指在编译时就能确定要调用的方法,通过函数重载和运算符重载、模板来实现。

动态多态:

● 动态多态是指在运行时根据对象的实际类型来确定要调用的函数,通过继承和函数覆盖来实现。

注意:本文中后续说的多态均为动态多态。

 多态的使用具有三个前提条件下面会进行详细介绍:

● 公有继承(继承权限为 public  )

● 函数覆盖

● 基类的指针/引用指向派生类的对象

函数覆盖

 函数覆盖、函数隐藏。这两个比较相似,但是函数隐藏不支持多态。而函数覆盖是多态的必要条件。函数覆盖和函数隐藏的区别有以下几点:

● 函数隐藏是派生类中存在与基类同名同参的函数,编译器会将基类的同名同参数的函数进行隐藏。

● 函数覆盖是基类中定义了一个虚函数,派生类编译写一个同名同参数的函数将基类中的虚函数进行重写并覆盖。注意:覆盖的函数必须是虚函数

 虚函数

虚函数(Virtual Function)是一种特殊类型的成员函数,用于实现多态性。虚函数可以在基类中声明,并在派生类中重新定义和实现。通过使用虚函数,可以实现基类指针或引用调用派生类对象的特定成员函数,从而实现运行时的动态绑定。

要将函数声明为虚函数,需要在函数声明前加上关键字  virtual 。基类中的虚函数的定义和实现是通用的,而派生类可以根据自己的需求对这些虚函数进行重写。

需要注意

虚函数具有传递性:基类中被覆盖的函数是虚函数,派生类中新覆盖的函数也是虚函数。

只有普通成员函数与析构函数可以被声明为虚函数

使用虚函数的步骤如下:

1、在基类中声明虚函数:在基类的函数声明前加上关键字 virtual ,如下所示:

class Base {
public:
    virtual void foo() {
        // 函数实现
    }
};

2、在派生类中重写虚函数:在派生类中实现与基类中虚函数具有相同名称和参数列表的函数,同时加上 override 关键字,明确表明这是对基类中虚函数的重写,如下所示:

class Derived : public Base {
public:
    void foo() override {
        // 函数实现
    }
};

多态的实现

我们在开篇时提到过,要实现多态,需要有三个前提条件:

● 公有继承(已经实现)

● 函数覆盖(已经实现)

● 基类的指针/引用指向派生类的对象(待实现)

为什么要基类的指针/引用指向派生类的对象?

● 实现运行时多态:当使用基类的指针或引用指向派生类的对象时,程序在运行时会根据对象的实际类型来调用相应的函数,而不是根据指针或者引用类型。

● 统一接口:基类的指针可以作为一个通用的接口,用于操作不同类型的派生类对象,这样可以使代码更灵活,减少重复的代码。并且的支持和拓展更好进行维护。

 使用方法如下:

#include <iostream>

using namespace std;

class Animal
{
public:

    virtual void eat()
    {
        cout << "动物爱吃饭" << endl;
    }
};

class Dog:public Animal
{
public:

    void eat()override
    {
        cout << "狗爱吃骨头" << endl;
    }
};

class Cat:public Animal
{
public:

    void eat()override
    {
        cout << "猫爱吃鱼" << endl;
    }
};

int main()
{
    // 基类指针指向派生类对象
    Animal *a1 = new Dog;
    // 调用派生类覆盖的虚函数
    a1->eat();  // 狗爱吃骨头

    Animal *a3 = new Cat;
    a3->eat();  // 猫爱吃鱼

    Dog d1;
    Animal &a2 = d1;
    a2.eat();   // 狗爱吃骨头

    return 0;
}

多态的原理 

实现多态性的机制是通过虚函数表 vtable 来实现的。每个包含虚函数的类都有一个虚函数表,其中存储了指向各个虚函数的指针。每个类的对象内部会有一个隐藏的虚函数表指针成员变量,指向当前类的虚函数表。当调用虚函数时,会根据对象的虚函数表中相应位置的指针找到正确的函数进行调用。

 虚析构函数

如果不使用虚析构函数,且基类的指针指向派生类的对象,使用delete销毁对象时,只能触发基类的析构函数,如果在派生类中申请内存等资源,则会导致内存无法释放,出现内存泄漏的问题。

解决方案是给基类的析构函数使用virtual修饰为虚析构函数,通过传递性可以把各个派生类的析构函数都变为虚析构函数,因此建议给一个可能为基类的类中的析构函数设置成虚析构函数。

示例如下:

#include <iostream>

using namespace std;

class Animal
{
public:

    virtual void eat()
    {
        cout << "动物爱吃饭" << endl;
    }
    // 虚析构函数
    virtual ~Animal()
    {
        cout << "Animal析构函数被调用了" << endl;
    }
};

class Dog:public Animal
{
public:

    void eat()override
    {
        cout << "狗爱吃骨头" << endl;
    }
    ~Dog()
    {
        cout << "Dog 析构函数被调用了" << endl;
    }
};

int main()
{
    // 基类指针指向派生类对象
    Animal *a1 = new Dog;
    // 调用派生类覆盖的虚函数
    a1->eat();  // 狗爱吃骨头

    delete a1;

    return 0;
}

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

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

相关文章

44岁过气港姐晚晚熬通宵开直播,情路坎坷生两胎老公身份成迷

曾经的「9料」港姐冠军杨思琦近年将工作重心转向内地&#xff0c;狠心抛下一儿一女在香港&#xff0c;只身一人定居广州靠当主播维持生计。 相信有不少网友都留意到&#xff0c;杨思琦几乎晚晚都通宵直播&#xff0c;睡觉前看她在卖力劲歌热舞与其他直播主PK赚钱&#xff0c;一…

AI大模型企业应用实战(25)-为Langchain Agent添加记忆功能

0 前言 在开发复杂的AI应用时,赋予Agent记忆能力是一个关键步骤。这不仅能提高Agent的性能,还能使其在多轮对话中保持上下文连贯性。本文将详细介绍如何在Langchain框架中为Agent添加记忆功能,并深入解析每个步骤的原理和最佳实践。 Agent记忆功能的核心组件 在Langchain中&…

ChatGPT的Mac客户端正式发布了

ChatGPT的Mac客户端正式发布了&#xff01;Mac用户有福了 &#x1f389; 大家好&#xff0c;我是猫头虎&#xff0c;科技自媒体博主。今天我带来了一个超级重磅的消息 &#x1f4e2;&#xff0c;就是 ChatGPT 的客户端终于来了&#xff01;这对我们所有 Mac 用户&#xff0c;尤…

2024国内外音频转换器大盘点,盘点音乐剪辑的7个有效方法!

当遇到不支持的音乐文件时&#xff0c;您可能就会想要拥有一款优秀的音频转换器。当您想减小大量音乐文件以节省设备存储空间时&#xff0c;它也可以很好地帮上忙。如果您正在寻找这么一款音频转换器&#xff0c;那么&#xff0c;请不要错过这篇文章。一款顶尖的音频转换器不仅…

2024年最新水利水电安全员(A证B证C证)考试题库

71.悬挑式操作平台可分为斜拉方式的悬挑式操作平台和下支承方式的悬挑式操作平台两种方式。下列关于悬挑式操作平台规定的说法中&#xff0c;错误的是&#xff08;&#xff09;。 A.悬挑式操作平台的搁置点.拉结点.支撑点应设置在主体结构上 B.悬挑式操作平台的悬挑长度不宜大…

【知识学习】阐述Unity3D中MaterialTexture的概念及使用方法示例

在Unity3D中&#xff0c;Material和Texture是渲染过程中非常重要的两个概念&#xff0c;它们共同工作以实现丰富的视觉效果。 Material Material是Unity中的一个组件&#xff0c;用于定义物体表面的视觉属性。一个Material可以包含多种属性&#xff0c;如颜色、纹理、反射率等…

Rocketmq在单节点情况下新增从节点

Rocketmq在单节点情况下新增从节点 在docker-compose部署rocketmq单节点的基础上&#xff0c;新增一个从节点 一&#xff0c;修改docker-compose配置文件 原docker-compose文件 version: 3.5 services:rmqnamesrv:image: foxiswho/rocketmq:server-4.5.2container_name: rm…

汽车软件开发者的必修课:ASPICE 4.0主要特点、优势及与之前版本的变化之处

ASPICE&#xff08;汽车SPICE&#xff09;4.0是专为汽车行业量身定制的过程评估模型&#xff0c;旨在确保软件和系统开发过程的质量和可靠性。它是更广泛的 ISO/IEC 330xx 系列标准的一部分&#xff0c;源自通用 SPICE&#xff08;软件流程改进和能力确定&#xff09;框架。 AS…

【数据结构】(C语言):栈

栈&#xff1a; 线性的集合。后进先出&#xff08;LIFO&#xff0c;last in first out&#xff09;。两个指针&#xff1a;指向栈顶和栈底。栈顶指向最后进入且第一个出去的元素。栈底指向第一个进入且最后一个出去的元素。两个操作&#xff1a;入栈&#xff08;往栈尾添加元素…

前端JS必用工具【js-tool-big-box】学习,根据属性对数组对象进行排序

我们时常遇到这样的场景&#xff0c;服务端给返回的一些数据呢&#xff0c;是json对象是无序的&#xff0c;或者说返回了一个数组&#xff0c;但里面的数据&#xff0c;前端需要根据一些业务需求做排序。 这一小节呢&#xff0c;我们就说一下&#xff0c;利用 js-tool-big-box …

Flask之表单

前言&#xff1a;本博客仅作记录学习使用&#xff0c;部分图片出自网络&#xff0c;如有侵犯您的权益&#xff0c;请联系删除 目录 一、HTML表单 二、使用Flask-WTF处理表单 2.1、定义WTForms表单类 2.2、输出HTML代码 2.3、在模板中渲染表单 三、处理表单数据 3.1、提…

武汉星起航:亚马逊全球化布局助力企业拓展国际市场

在当今全球化经济的大背景下&#xff0c;企业如何突破地域限制&#xff0c;将产品推向更广阔的市场&#xff0c;成为了摆在众多企业家面前的重要课题。武汉星起航相信&#xff0c;亚马逊&#xff0c;作为全球最大的在线零售平台之一&#xff0c;以其独特的全球化布局和强大的服…

nuget 包修改默认存放路径

平时使用 nuget packages 时&#xff0c;都是下载包文件到本地。 默认是在C盘&#xff0c;时间一久容量会高达几十个G&#xff0c;这样会拖慢系统运行效率。 这时需要修改包的下载位置。 打开nuget 包配置文件&#xff1a;Nuget.config 路径在 C:\Users\{UserName}\AppData…

一年Java|16K|同程艺龙面经

面经哥只做互联网社招面试经历分享&#xff0c;关注我&#xff0c;每日推送精选面经&#xff0c;面试前&#xff0c;先找面经哥 背景 公司&#xff1a;同程艺龙成都BU,现场部门老大面 之前的同程艺龙电话一面过了&#xff0c;然后通知到同程艺龙成都办公地点现场进行部门老大…

RK3588 Android13 TvSetting 中性能浮窗RAM显示bug

前言 电视产品,客户发现在设备偏好设置->高级设置->性能浮窗菜单里显示的 RAM 大小是错误的, 要求改成正确的,并且屏幕密度修改后,这个浮窗显示不全,也需要一起处理。 效果图 TvSetting 部分修改文件清单 bug 原因在于 Formatter.formatFileSize 这个 API,我们…

ATA-7025高压放大器的优势如何

高压放大器是一类在电子领域中具有重要作用的设备&#xff0c;其主要功能是将输入信号的电压放大到更高的水平。在许多应用中&#xff0c;高压放大器展现出独特的优势&#xff0c;下面将介绍高压放大器的优势以及它们在不同领域的应用。 高压放大器的优势 1.信号驱动能力强 高压…

探索AI世界系列:俗说AI智能体

AI agent&#xff0c;翻译为中文就是AI智能体。 什么是AI智能体呢&#xff1f; 一&#xff0c;GPT对AI智能体的定义 AI智能体&#xff0c;即人工智能体&#xff08;Artificial Intelligence Agent&#xff09;&#xff0c;是具有自主性、学习能力和推理能力的计算机程序。 …

常用的企业级快速传输大文件平台

在当今企业运营中&#xff0c;数据管理成了一项不可或缺的任务。企业每日需处理庞大的数据量&#xff0c;这包括高清视频、大量数据集和复杂的设计图纸等大型文件。然而&#xff0c;传统的文件传输手段&#xff0c;比如通过电子邮件发送附件或使用FTP服务&#xff0c;已经难以满…

【C++】关于虚函数的理解

深入探索C虚函数&#xff1a;原理、应用与实例分析 一、虚函数的原理二、虚函数的应用三、代码实例分析四、总结 在C面向对象编程的世界里&#xff0c;虚函数&#xff08;Virtual Function&#xff09;扮演着至关重要的角色。它不仅实现了多态性这一核心特性&#xff0c;还使得…

充电宝怎么选合适?买充电宝必看选购攻略!好用充电宝推荐

在这个科技飞速发展的时代&#xff0c;手机、平板等电子设备已经成为我们生活中不可或缺的一部分。然而&#xff0c;电池续航问题却常常困扰着我们&#xff0c;特别是在外出旅行、出差或者日常通勤中。这时候&#xff0c;一个靠谱的充电宝就显得尤为重要。但是&#xff0c;面对…