【C++】多态的概念和简单介绍、虚函数、虚函数重写、多态构成的条件、重载、重写、重定义

news2024/11/25 10:40:39

文章目录

  • 多态
    • 1.多态的概念和介绍
    • 2.虚函数
      • 2.1final
      • 2.2override
    • 3.虚函数的重写
      • 3.1协变
      • 3.2析构函数的重写
    • 4.多态构成的条件
    • 5.重载、重写、重定义
    • ......

多态

1.多态的概念和介绍

  C++中的多态是一种面向对象编程的特性,它允许不同的对象对同一个消息做出不同的响应。 多态性能够提高代码的可复用性和灵活性,使得代码更加模块化和可扩展。

  多态性是通过使用继承和虚函数实现的。 当一个类被声明为虚函数时,它可以被子类重写,并且在运行时根据对象的实际类型来调用相应的函数。这种动态绑定的特性使得程序能够根据不同的对象类型来执行不同的操作。

  简单总结:就是在完成某个行为时,不同的对象会产生出不同的状态。

在这里插入图片描述

             
  通过多态我们可以模拟实现不同种动物的叫声:

#include <iostream>
using namespace std;

// 基类
class Animal {
public:
    virtual void makeSound() {
        cout << "Animal makes sound" << endl;
    }
};

// 派生类
class Cat : public Animal {
public:
    virtual void makeSound() {
        cout << "meows" << endl;
    }
};

class Dog : public Animal {
public:
    virtual void makeSound() {
        cout << "barks" << endl;
    }
};

class Capybara : public Animal {
public:
    virtual void makeSound() {
        cout << "ka~pi~ba~la~" << endl;
    }
};

int main() {
    Animal* animal1 = new Dog();
    Animal* animal2 = new Cat();
    Animal* animal3 = new Capybara();

    animal1->makeSound(); // 调用Dog类的makeSound函数
    animal2->makeSound(); // 调用Cat类的makeSound函数
    animal3->makeSound(); // 调用Capybara类的makeSound函数

    delete animal1;
    delete animal2;
    delete animal3;
        
    return 0;
}

//meows
//barks
//ka~pi~ba~la~

              

2.虚函数

  虚函数:即被virtual修饰的类成员函数称为虚函数。

  虚函数是为了实现动态绑定和多态性。当基类的指针或引用指向派生类对象时,通过调用虚函数,可以根据对象的实际类型来确定调用哪个函数。这样在运行时才确定函数的调用,而不是在编译时确定。

  虚函数的定义如下:

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

  需要注意的是,虚函数可以是纯虚函数,即在基类中只有函数的声明而没有实现。 纯虚函数的定义如下:

  包含纯虚函数的类被称为抽象类,抽象类不能被实例化,只能作为基类来派生其他类。派生类必须重写纯虚函数才能被实例化。

class Base {
public:
    virtual void function() = 0; // 纯虚函数
};

  

  C++对函数重写的要求比较严格,但是有些情况下由于疏忽,可能会导致函数名字母次序写反而无法构成重载,而这种错误在编译期间是不会报出的,只有在程序运行时没有得到预期结果才来debug会得不偿失,所以:C++11提供了override和final两个关键字,可以帮助用户检测是否重写。

2.1final

  final:修饰虚函数,表示该虚函数不能再被重写。

class Car
{
public:
	virtual void Drive() final {}
};

class Benz :public Car
{
public:
	virtual void Drive() {cout << "Benz-舒适" << endl;}
};

2.2override

  override: 检查派生类虚函数是否重写了基类某个虚函数,如果没有重写编译报错。

class Car{
public:
	virtual void Drive(){}
};

class Benz :public Car {
public:
	virtual void Drive() override {cout << "Benz-舒适" << endl;}
};

              

3.虚函数的重写

  虚函数的重写是指在派生类中重新定义基类中已经声明为虚函数的函数。通过重写虚函数,派生类可以根据自己的需要重新实现该函数,从而覆盖基类中的实现。

  虚函数的重写(覆盖):派生类中有一个跟基类完全相同的虚函数(即派生类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同)。

  下面的代码就构成重写:

// 基类
class Animal {
public:
    virtual void makeSound() {
        cout << "Animal makes sound" << endl;
    }
};

// 派生类
class Cat : public Animal {
public:
    virtual void makeSound() {
        cout << "meows" << endl;
    }
};

在这里插入图片描述

3.1协变

  协变:基类与派生类虚函数返回值类型不同。

  派生类重写基类虚函数时,与基类虚函数返回值类型不同。即基类虚函数返回基类对象的指针或者引用,派生类虚函数返回派生类对象的指针或者引用时,称为协变。

class A{};
class B : public A {};

class Person {
public:
	virtual A* f() {return new A;}
};

class Student : public Person {
public:
	virtual B* f() {return new B;}
};

3.2析构函数的重写

  析构函数的重写:基类与派生类析构函数的名字不同。

  如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的析构函数构成重写,虽然基类与派生类析构函数名字不同。虽然函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor。

  析构函数可以被重写,其语法与普通函数的重写相同。派生类可以在其定义中重写基类的析构函数,以便在对象销毁时执行特定的清理操作。

  下面是一个示例代码,演示了如何在派生类中重写基类的析构函数:

#include <iostream>

class Base {
public:
    virtual ~Base() {
        std::cout << "Base destructor" << std::endl;
    }
};

class Derived : public Base {
public:
    ~Derived() override {
        std::cout << "Derived destructor" << std::endl;
    }
};

int main() {
    Base* base = new Derived();
    delete base;

    return 0;
}
//Derived destructor
//Base destructor

              

4.多态构成的条件

  多态性的实现需要满足以下条件:

  (1)存在继承关系: 多态性是通过基类和派生类之间的继承关系来实现的。派生类继承了基类的成员函数和虚函数。

  (2)使用基类指针或引用来调用派生类对象: 通过将基类指针或引用指向派生类对象,可以实现多态性。通过基类指针或引用调用虚函数时,实际调用的是派生类中重写的函数。

  (3)基类中存在虚函数:在基类中声明一个虚函数,使得它可以在派生类中被重写。 通过将基类的成员函数声明为虚函数,可以实现动态绑定,即在运行时根据对象的实际类型来确定调用哪个函数。

  对于上面的代码就满足虚函数的重写:

在这里插入图片描述
             
  简单的多态实现的例子:

  Shape类是一个基类,它包含一个虚函数draw。Circle类和Rectangle类分别是Shape类的派生类,它们重写了draw函数。

  在主函数中,我们创建了一个指向Circle对象的Shape指针和一个指向Rectangle对象的Shape指针。当调用shape1->draw()时,由于shape1指向的是Circle对象,所以会调用Circle类的draw函数,输出"Drawing a circle"。

  同样地,当调用shape2->draw()时,由于shape2指向的是Rectangle对象,所以会调用Rectangle类的draw函数,输出"Drawing a rectangle"。

#include <iostream>
using namespace std;

// 基类
class Shape {
public:
    virtual void draw() {
        cout << "Drawing a shape" << endl;
    }
};

// 派生类
class Circle : public Shape {
public:
    void draw() {
        cout << "Drawing a circle" << endl;
    }
};

class Rectangle : public Shape {
public:
    void draw() {
        cout << "Drawing a rectangle" << endl;
    }
};

int main() {
    Shape* shape1 = new Circle();
    Shape* shape2 = new Rectangle();

    shape1->draw(); // 调用Circle类的draw函数
    shape2->draw(); // 调用Rectangle类的draw函数

    delete shape1;
    delete shape2;

    return 0;
}

//Drawing a circle
//Drawing a rectangle

  总结多态的构成条件:

  (1)存在继承;

  (2)必须通过基类的指针或者引用调用虚函数;

  (3)被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写。

              

5.重载、重写、重定义

  重载(Overload)、重写(Override)和重定义(Hide)是面向对象编程中的三个概念,用于描述派生类对基类成员的处理方式。

  (1)重载(Overload): 重载是指在同一个作用域内,根据函数名相同但参数列表不同的规则,定义多个具有相同名称但参数不同的函数。 重载函数可以有不同的返回类型,但不能仅通过返回类型的不同来进行重载。在调用重载函数时,编译器会根据函数调用的参数类型和数量来确定调用哪个重载函数。

  (2)重写(Override): 重写是指在派生类中重新定义基类的虚函数。派生类中的重写函数具有相同的函数名、参数列表和返回类型,它们用于替代基类中的虚函数。 在运行时,通过基类指针或引用调用虚函数时,会根据实际对象的类型调用相应的派生类的重写函数。重写函数可以通过使用override关键字来显式地标记。

  (3)重定义(Hide): 重定义是指在派生类中定义一个与基类中的非虚函数同名的函数。 在派生类中的重定义函数与基类中的函数没有关联,它们是完全独立的函数。在运行时,通过基类指针或引用调用函数时,会根据指针或引用的类型调用相应的函数。重定义函数不需要使用特定的关键字进行标记。

在这里插入图片描述

当然多态还有很多的知识点,这里只是对C++多态的部分介绍了😉
如有错误❌望指正,最后祝大家学习进步✊天天开心✨🎉

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

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

相关文章

1475.商品折扣后的最终价格

文章目录 题目描述解题思路&#xff1a;方法一&#xff1a;通俗解法方法二&#xff1a;单调栈 leetcode原题链接 1475. 商品折扣后的最终价格 题目描述 给你一个数组 prices &#xff0c;其中 prices[i] 是商店里第 i 件商品的价格。 商店里正在进行促销活动&#xff0c;如果你…

linux安装oracle11g

linux安装oracle11g 环境&#xff1a; redhat7 版本 11.2.0.4 一、部署环境准备 1.1 关闭selinux [rootlocalhost software]# vi /etc/selinux/config # This file controls the state of SELinux on the system. # SELINUX can take one of these three values: # enforci…

JavaFx基础学习【五】:FXML布局文件使用

一、介绍 FXML是一种在JavaFX应用程序中定义用户界面的&#xff0c;基于XML的声明性标记语言。FXML非常适用来静态布局&#xff0c;如表单、控件和表格。 如果你还不是不明白FXML到底是什么&#xff0c;你可以类比HTML&#xff0c;我们可以通过HTML来做web页面的UI&#xff0…

全文检索与日志管理 Elasticsearch(上)

一、Elasticsearch介绍 1.1 全文检索索引 Elasticsearch是一个全文检索服务器&#xff0c;全文检索是一种非结构化数据的搜索方式。 那么什么是结构化数据和非结构化数据呢&#xff1f; 结构化数据&#xff1a;指具有固定格式固定长度的数据&#xff0c;如数据库中的字段。 …

【RH850/U2A】:DMA开发笔记

DMA开发笔记 项目背景参考文档DMA开发过程开发过程中的问题汇总框图预览设计思路重点注意DMA的功能安全属性串口的DMA请求信号模式选择配置DMA的中断如果我们买了第三方的模块(比如LIN/UART)它是自带DMA配置开启功能;奈何,我们没有购买第三方模块所以需要我们参考datasheet…

Python Opencv实践 - 图像放射变换

import cv2 as cv import numpy as np import matplotlib.pyplot as pltimg cv.imread("../SampleImages/pomeranian.png", cv.IMREAD_COLOR) rows,cols img.shape[:2] print(img.shape[:2])#使用getAffineTransform来获得仿射变换的矩阵M #cv.getAffineTransform(…

(白帽黑客)大厂面试题

以下为网络安全各个方向涉及的面试题&#xff0c;星数越多代表问题出现的几率越大&#xff0c;祝各位都能找到满意的工作。 注&#xff1a;本套面试题&#xff0c;已整理成pdf文档&#xff0c;但内容还在持续更新中&#xff0c;因为无论如何都不可能覆盖所有的面试问题&#xf…

Django之定时任务--apscheduler

Django--定时任务apscheduler的使用 apscheduler定时任务的使用1、安装包2、配置settings.py3、在manage.py的文件同级目录下创建文件scheduler.py4、在项目的urls.py中调用这个定时计划5、然后启动项目 python manage.py runserver,在admin中查看就能看到你的定时任务及执行的…

LeetCode刷题——617. 合并二叉树

617. 合并二叉树 给你两棵二叉树&#xff1a; root1 和 root2 。 想象一下&#xff0c;当你将其中一棵覆盖到另一棵之上时&#xff0c;两棵树上的一些节点将会重叠&#xff08;而另一些不会&#xff09;。你需要将这两棵树合并成一棵新二叉树。合并的规则是&#xff1a;如果两…

嵌入式:ARM Day1

1. 思维导图 2.作业一 3.作业2

selenium自动化测试之搭建测试环境

自动化测试环境&#xff1a; Python3.7Selenium3.141谷歌浏览器76.0/火狐浏览器 1、安装Python并配置环境变量。 下载并安装&#xff1a;配置环境变量&#xff1a;C:\Python37;C:\Python37\Scripts; 2、安装Pycharm开发工具。 下载地址&#xff1a; 注意下载&#xff1a;Co…

stm32项目(10)——基于stm32的盲人监护系统

一.实现的功能 本次设计的盲人监护系统&#xff0c;旨在为盲人的外出提供保护。主要功能如下&#xff1a; 超声波测距模块检测前方障碍物&#xff0c;当前方有障碍物时&#xff0c;语音模块报警提示“前方有障碍物&#xff0c;请绕道”&#xff0c;盲人在听到这条语音后就知…

一篇文章搞懂图像的本质是什么

图像是什么&#xff1f; 每个图⽚可以看成数组 彩⾊图⽚呢&#xff1f; #pic_center 500x500 计算机眼⾥的颜⾊图⽚ Numpy读取彩⾊照⽚ • Shape 三维数组 • ⾼度 • 宽度 • 颜⾊通道 Numpy读取彩⾊照⽚ • 照⽚⼤⼩ 540 x 480 • 540个像素宽度 • 480个像素⾼度 • 3个颜…

[NDK]从Opengles到Vulkan-基础篇(2)-运行配置

上一篇我们介绍了Opengl和Vulkan运行环境的不同。 引入Opengles,我们需要做的是,在Cmakes中配置动态库引入。 使用opengles2就用GLESv2,用es3就用GLESv3,而EGL需要使用配置EGL环境 这里两个比较基础的东西是EGL和GLES的库引入。 es2只要Android 4.0就开始支持,es3是4.4开…

【2023最新版】APP测试面试题(超详细~)

一、web测试和app测试的相同点和区别&#xff1f; 相同点&#xff1a;都离不开测试的基础知识和测试原理。具体包括以下几个方面。 测试用例&#xff0c;均使用边界值分析法&#xff0c;等价类划分法等。多数采用黑盒测试&#xff0c;来验证业务功能是否能得到正确的应用。 需…

外贸出货中惹了个大麻烦

谈C端的客户与B端的客户相比&#xff0c; 我觉得最大的好处就是不用我们自己去报关&#xff0c;一般C端的客户都会有自己的货代负责双清业务&#xff0c;即使我们自己去发货&#xff0c; 也是会找双清的货代&#xff0c;因此我们自己本身就不要报关报检&#xff0c; 只需要将货…

【云原生】K8S存储卷:PV、PVC详解

目录 一、emptyDir存储卷二、hostPath存储卷三、nfs共享存储卷四、PVC 和 PV4.1 NFS使用PV和PVC4.2创建动态PV 一、emptyDir存储卷 容器磁盘上的文件的生命周期是短暂的&#xff0c;这就使得在容器中运行重要应用时会出现一些问题。首先&#xff0c;当容器崩溃时&#xff0c;ku…

Web菜鸟教程 - Radis实现高性能数据库

Redis是用C语言开发的一个高性能键值对数据库&#xff0c;可用于数据缓存&#xff0c;主要用于处理大量数据的高访问负载。 也就是说&#xff0c;如果你对性能要求不高&#xff0c;不用Radis也是可以的。不过作为最自己写的程序有高要求的程序员&#xff0c;自然是要学一下的&a…

adb对安卓app进行抓包(ip连接设备)

adb对安卓app进行抓包&#xff08;ip连接设备&#xff09; 一&#xff0c;首先将安卓设备的开发者模式打开&#xff0c;提示允许adb调试 二&#xff0c;自己的笔记本要和安卓设备在同一个网段下&#xff08;同连一个WiFi就可以了&#xff09; 三&#xff0c;在笔记本上根据i…

RunnerGo的相比较JMeter优势,能不能替代?

目前在性能测试领域市场jmeter占有率是非常高的&#xff0c;主要原因是相对比其他性能测试工具使用更简单&#xff08;开源、易扩展&#xff09;&#xff0c;功能更强大&#xff08;满足多种协议的接口&#xff09;&#xff0c;但是随着研发协同的升级&#xff0c;平台化的性能…