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

文章目录

  • 多态
    • 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.rhkb.cn/news/90559.html

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

相关文章

Hazel 引擎学习笔记

目录 Hazel 引擎学习笔记学习方法思考引擎结构创建工程程序入口点日志系统Premake\MD没有 cpp 文件的项目会出错include 到某个库就要包含这个库的路径&#xff0c;注意头文件展开 事件系统 获取和利用派生类信息预编译头文件抽象窗口类和 GLFWgit submodule addpremake 脚本禁…

【JVM】对String::intern()方法深入详解(JDK7及以上)

文章目录 1、什么是intern&#xff1f;2、经典例题解释例1例2例3 1、什么是intern&#xff1f; String::intern()是一个本地方法&#xff0c;它的作用是如果字符串常量池中已经包含一个等于此String对象的字符串&#xff0c;则返回代表池中这个字符串的String对象的引用&#…

7-15 然后是几点

有时候人们用四位数字表示一个时间&#xff0c;比如 1106 表示 11 点零 6 分。现在&#xff0c;你的程序要根据起始时间和流逝的时间计算出终止时间。 读入两个数字&#xff0c;第一个数字以这样的四位数字表示当前时间&#xff0c;第二个数字表示分钟数&#xff0c;计算当前时…

【Vue-Router】嵌套路由

footer.vue <template><div><router-view></router-view><hr><h1>我是父路由</h1><div><router-link to"/user">Login</router-link><router-link to"/user/reg" style"margin-left…

代码随想录算法训练营(二叉树总结篇)

一.二叉树的种类 1.满二叉树&#xff1a;就是说每一个非叶子节点的节点都有两个子节点。 2.完全二叉树&#xff1a;此二叉树只有最后一层可能没填满&#xff0c;并且存在的叶子节点都集中在左侧&#xff01;&#xff01;&#xff01; &#xff08;满二叉树也是完全二叉树&…

【Flutter】【基础】CustomPaint 绘画功能(一)

功能&#xff1a;CustomPaint 相当于在一个画布上面画画&#xff0c;可以自己绘制不同的颜色形状等 在各种widget 或者是插件不能满足到需求的时候&#xff0c;可以自己定义一些形状 使用实例和代码&#xff1a; CustomPaint&#xff1a; 能使你绘制的东西显示在你的ui 上面&a…

安装Tomac服务器——安装步骤以及易出现问题的解决方法

文章目录 前言 一、下载Tomcat及解压 1、选择下载版本&#xff08;本文选择tomcat 8版本为例&#xff09; 2、解压安装包 二、配置环境 1、在电脑搜索栏里面搜索环境变量即可 2、点击高级系统设置->环境变量->新建系统变量 1) 新建系统变量&#xff0c;变量名为…

nginx一般轮询、加权轮询、ip_hash等负载均衡模式配置介绍

一.负载均衡含义简介 二.nginx负载均衡配置方式 准备三台设备&#xff1a; 2.190均衡服务器&#xff0c;2.191web服务器1&#xff0c;2.160web服务器2&#xff0c;三台设备均安装nginx&#xff0c;两台web服务器均有网页内容 1.一般轮询负载均衡 &#xff08;1&#xff09…

Autoware感知02—欧氏聚类(lidar_euclidean_cluster_detect)源码解析

文章目录 引言一、点云回调函数&#xff1a;二、预处理&#xff08;1&#xff09;裁剪距离雷达过于近的点云&#xff0c;消除车身的影响&#xff08;2&#xff09;点云降采样&#xff08;体素滤波&#xff0c;默认也是不需要的&#xff09;&#xff08;3&#xff09;裁剪雷达高…

React Native 图片组件基础知识

在 React Native 中使用图片其实跟 HTML 中使用图片一样简单&#xff0c;在 React Native 中我们使用Image组件来呈现图片的内容&#xff0c;其中主要的属性有&#xff1a;source。这个属性主要是设置图片的内容&#xff0c;它可以是网络图像地址、静态资源、临时本地图像以及本…

【LeetCode75】第二十九题 删除链表的中间节点

目录 题目&#xff1a; 示例; 分析: 代码: 题目&#xff1a; 示例; 分析: 给我们一个链表&#xff0c;让我们把链表中间的节点删了。 那么最直观最基础的办法是遍历两边链表&#xff0c;第一遍拿到链表长度&#xff0c;第二次把链表中间节点删了。 这个暴力做法我没事过…

Docker查看、创建、进入容器相关的命令

1.查看、创建、进入容器的指令 用-it指令创建出来的容器&#xff0c;创建完成之后会立马进入容器。退出之后立马关闭容器。 docker run -it --namec1 centos:7 /bin/bash退出容器&#xff1a; exit查看现在正在运行的容器命令&#xff1a; docker ps查看历史容器&#xff0…

Uniapp当中使用腾讯位置路线规划插件保姆教学

首先我们在使用腾讯地图插件之前我们需要先做几点准备 1&#xff1a;我们需要在腾讯地图位置服务当中注册账号以及在控制台当中创建应用和创建key 这里在创建应用当中应用类型一定要选出行类型&#xff0c;否则后期可能会出现问题。 我们创建完应用之后&#xff0c;点击创建…

java毕业设计-智慧食堂管理系统-内容快览

首页 智慧食堂管理系统是一种可以提高食堂运营效率的管理系统。它将前端代码使用Vue实现&#xff0c;后端使用Spring Boot实现。这个系统的目的是简化食堂管理&#xff0c;提高食堂服务质量。在现代快节奏的生活中&#xff0c;人们对餐饮服务提出了更高的要求&#xff0c;食堂管…

论文复现--关于单视角动作捕捉工具箱--MMHuman3d的研究(基于Windows10和Linux18.04中配置)

分类&#xff1a;动作捕捉 github地址&#xff1a;https://github.com/open-mmlab/mmhuman3d 所需环境&#xff1a; Windows10&#xff0c;CUDA11.6&#xff0c;conda 4.13.0&#xff0c;Visual Studio 2017&#xff1b; Ubuntu18.04&#xff0c;conda22.9.0&#xff0c;CUDA11…

C语言题目的多种解法分享 2之字符串左旋和补充题

前言 有的时候&#xff0c;这个系列专栏中的解法之间并无优劣&#xff0c;只是给大家提供不同的解题思路 我决定将代码实现的过程写成注释&#xff0c;方便大家直接找到对应的函数&#xff0c;只有需要补充说明的知识才会单拿出来强调 这个系列的文章会更的比较慢&#xff0…

Kafka3.0.0版本——Broker( 退役旧节点)示例

目录 一、服务器信息二、先启动4台zookeeper&#xff0c;再启动4台kafka三、通过PrettyZoo工具验证启动的kafka是否ok四、查看4台kafka集群节点上是否存在创建的名称为news的主题五、退役旧节点5.1、执行负载均衡操作5.2、 执行停止命令5.3、再次查看kafka中的创建过的名称为ne…

React Native 列表组件基础知识

ScrollView 组件 ScrollView组件是一个容器滚动组件&#xff0c;当容器超出指定宽高时就可以进行滚动交互。 ScrollView组件是一次性渲染所有的 React 子组件&#xff0c;这在性能上是比较差的&#xff0c;所以不建议当列表特别长的时候使用此组件。 接下来列举几个常用的一…

导入示例工程出现error: failed to start ability. Error while Launching activity错误的解决办法

导入华为健康生活应用&#xff08;ArkTS&#xff09;&#xff0c;使用DevEco Studio打开&#xff0c;运行报错&#xff1a; error: failed to start ability. Error while Launching activity解决办法&#xff1a;修改module.json5里面exported的值&#xff0c;由false改为tr…

LVGL学习笔记 30 - List(列表)

目录 1. 添加文本 2. 添加按钮 3. 事件 4. 修改样式 4.1 背景色 4.2 改变项的颜色 列表是一个垂直布局的矩形&#xff0c;可以向其中添加按钮和文本。 lv_obj_t* list1 lv_list_create(lv_scr_act());lv_obj_set_size(list1, 180, 220);lv_obj_center(list1); 部件包含&…