【C++从小白到大牛】多态那些事儿(上)

news2024/11/20 21:19:44

一、多态的概念

1.1概念:

通俗来说,就是多种形态,具体点就是去完成某个行为,当不同的对象去完成时会产生出不同的状态。

二、 多态的定义及实现

 2.1多态的构成条件

多态是在不同继承关系的类对象,去调用同一函数,产生了不同的行为。比如Student继承了Person。Person对象买票全价,Student对象买票半价。

在继承中要多态还要两个条件

  1. 父类指针或引用去调用虚函数,这样才能保证传父类对象调用的就是父类的虚函数,传子类对象,调用的是子类的虚函数(调用子类传子类利用切片的原理)
  2. 虚函数完成重写:父子类中的两个虚函数,三同(函数名、参数、返回值)这样父子类的两个虚函数才能构成重写

可以将重写理解为隐藏的子集,因为隐藏仅要求函数名相同

 2.2虚函数

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

class Person {
public:
 virtual void BuyTicket() { cout << "买票-全价" << endl;}
};

2.3虚函数的重写

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

class Person {
public:
 virtual void BuyTicket() { cout << "买票-全价" << endl; }
};
class Student : public Person {
public:
 virtual void BuyTicket() { cout << "买票-半价" << endl; }
/*注意:在重写基类虚函数时,派生类的虚函数在不加virtual关键字时,虽然也可以构成重写(因
为继承后基类的虚函数被继承下来了在派生类依旧保持虚函数属性),但是该种写法不是很规范,不建议这样使用*/
/*void BuyTicket() { cout << "买票-半价" << endl; }*/
};
void Func(Person& p)
{ p.BuyTicket(); }

2.4虚函数重写的两个例外:

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;}
};

2. 析构函数的重写(基类与派生类析构函数的名字不同)

普通调用:看指针或者引用或者对象的类型

多态调用:看指针或者引用指向的对象

我们希望上面的特殊情况是多态调用,如果是普通调用,会造成内存泄漏(student对象没有析构,如下图)。

所以我们如何才能变成多态调用呢?

在子类和基类的析构函数都加上virtual构成重写,变为多态调用。那这里违反重写的规则,函数名都不相同,怎么能构成重写呢?

答:

虽然函数名不相同,看起来违背了重写的规则,其实不然,这里可以理解为编译器对析构函数的名称做了特殊处理,编译后析构函数的名称统一处理成destructor

TIP:特殊情况

如果基类的析构函数为虚函数,此时派生类析构函数只要定义,无论是否加virtual关键字,都与基类的析构函数构成重写

2.5、关于父类子类virtual加与不加

虚函数重写时,父类虚函数加了virtual,子类不加,也构成重写;但是如果父类不加virtual,就不构成重写。

建议:两个虚函数都加上virtual

2.6 C++11 override 和 final

final 关键字的两个作用:

其一是final修饰的类是最终类,不能被继承

注意实现一个类,这个类不能被继承还有一种方法:让父类构造函数私有化,派生类实例化不出对象。

其二是修饰虚函数,表示该虚函数不能再被重写

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

2.7重载、覆盖(重写)、隐藏(重定义)的对比

重载:

  1. 两个函数在同一作用域
  2. 函数名相同,参数不同

重写(覆盖)

  1. 两个函数分别在基类和派生类的作用域
  2. 函数名/参数/返回值都必须相同(协变例外)
  3. 两个函数都必须是虚函数

重定义(隐藏)

  1. 两个函数分别在基类和派生类的作用域
  2. 函数名相同
  3. 两个基类和派生类的同名函数不构成重写就是重定义

四、多态的原理

4.1虚函数表

先做一道笔试题:

// 这里常考一道笔试题:sizeof(Base)是多少?
class Base
{
public:
 virtual void Func1()
 {
 cout << "Func1()" << endl;
 }
private:
 int _b = 1;
};

通过观察测试我们发现b对象是8bytes。

除了_b成员,还多一个__vfptr指针,也就是虚函数表指针,这个表本质上是一个函数指针的数组

vfptr是存放函数指针的数组,就是将虚函数的指针存进去。

虚函数的重写也叫做覆盖,重写是语法层的概念,覆盖是原理层的概念。

形象的记忆:

比如上图,子类将父类的拷贝过来,然后虚表重写的部分将原先父类虚表的部分进行一个覆盖

具体如何实现多态调用和普通调用?

  • 多态调用:

运行时去虚函数表中找函数的地址,进行调用,所以指向父类调用的是父类虚函数,指向子类调用的是子类虚函数。

  • 普通调用:

编译时,通过调用者类型确定函数地址。

面试题解析:

解析:

首先我们看到B继承A,那么B里面的func函数和A里面的func函数构不构成重写呢?

函数名相同,返回值相同,参数类型相同(注意看参数是否相同,就是看类型,与变量名、缺省值无关!)并且父类是虚函数,所以构成重写!

接着我们看到p->test(),直接调用到了父类test()里面的func()函数,那么这里this指针是A* or B*呢,因为此时的test()是在父类,因此是A*,(如果是B*子类,那就不满足多态的条件必须是父类,所以不满足多态)所以这里的func函数构成了多态,因此是多态调用,所以是指针/引用指向的类型,因此调用B里面的func函数,所以答案是D嘛?

但真正的答案是B。

原因是多态调用,重写是实现重写,会将父类的函数声明与子类进行组合,因此val的值就是父类的1,因此答案是B!

下面因为是子类的调用,不构成多态,因此答案全都是D

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

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

相关文章

flutter开发windows应用程序(.exe)配置环境搭建 以及 visual studio配置flutter windows所需环境

flutter 开发 windows 应用程序&#xff08;.exe&#xff09;配置环境搭建 安装 android studio 开发工具 并配置相关所需环境 参考该文章&#xff1a;我还没写完你上百度上搜一下吧我帮你付钱了&#x1f602; 控制台运行 flutter doctor 命令 查看当前 flutter 开发环境 报…

傅里叶级数的数学描述

目录 概述 1 傅里叶级数 1.1 概念 1.2 表示形式 2 傅里叶变换 2.1 概念 2.2 数学描述 2.3 应用 3 傅里叶级数的数学推论 3.1 三角函数的正交性 3.1.1 正交性介绍 3.1.2 正交性证明 3.1.3 相同函数乘积积分 3.2 理论介绍 3.3 傅里叶级数的表述 概述 傅里叶级数是…

sais复杂推理能力评估笔记(一):baseline简介

赛题流程 初赛数据集为逻辑推理数据&#xff0c;其中训练集中包含500条训练数据&#xff0c;测试集中包含500条测试数据。每个问题包括若干子问题&#xff0c;每个子问题为单项选择题&#xff0c;选项不定&#xff08;最多5个&#xff09;。目标是为每个子问题选择一个正确答案…

前端需要了解的数据库基础与身份认证

目录 数据库与身份认证数据库的基本概念常见数据库和分类传统型数据库的数据组织结构安装并配置MySQL MySQL的基本使用使用SQL管理数据库SQL中的SELECT语句SQL中的INSERT INTO语句SQL中的UPDATE语句SQL中的DELETE语句SQL中的WHERE子句SQL中的AND和OR运算符SQL中的ORDER BY子句S…

Pip 使用报错及解决

pip install 是Python 包管理器命令&#xff0c;常用参数&#xff1a; -r&#xff1a;从一个需求文件中安装所有的包。-U 或 --upgrade&#xff1a;升级一个已经安装的包到最新版本。-I 或 --ignore-installed&#xff1a;即使包已经安装&#xff0c;也重新安装。--no-cache-d…

奥运会Ⅴ--具有混合模型的 Transformer 架构

Transformer 架构的变革性影响和混合模型的未来&#xff0c;将 Transformer 与其他架构相结合&#xff0c;突破 AI 能力的界限。 Transformer 架构从根本上重塑了自然语言处理 (NLP) 和机器学习的格局。这种序列建模和传导任务的创新方法不仅超越了之前最先进的模型&#xff0c…

每日OJ_牛客WY15 幸运的袋子

目录 牛客HJ62 查找输入整数二进制中1的个数 解析代码 牛客HJ62 查找输入整数二进制中1的个数 查找输入整数二进制中1的个数_牛客题霸_牛客网 解析代码 本题是计算一个数二进制表示中1的个数&#xff0c;通过&#xff08;n >> i) & 1可以获取第i位的二进制值&…

阿里云文件上传之客户端上传

阿里云文件上传之前一直是使用服务端上传,但一直存在上传不稳定问题,三兆以上的文件上传经常出现上传超时问题.究其原因客户端将文件上传到业务服务器&#xff0c;然后业务服务器将文件上传到OSS。在这个过程中&#xff0c;一份数据需要在网络上传输两次&#xff0c;会造成网络…

Vuforia AR篇(九)— AR塔防下篇

目录 前言一、搭建UI二、创建脚本 前言 在增强现实&#xff08;AR&#xff09;技术快速发展的今天&#xff0c;Vuforia作为一个强大的AR开发平台&#xff0c;为开发者提供了许多便捷的工具和功能。在本篇博客中&#xff0c;我们将介绍如何使用Vuforia在Unity中创建一个简单的塔…

46 class添加与颜色分配47 区域规则介绍与添加48 走线修线介绍49 复制、改变、删除操作

46 class添加与颜色分配&&47 区域规则介绍与添加&&48 走线修线介绍&&49 复制、改变、删除操作 第一部分 46 class添加与颜色分配创建网络类CLS创建网络组net-group NG颜色分配**填充类型设置****高亮关闭****修改颜色面板的颜色**从其它已有PCB设计中导…

用Manim实现——计算和绘制图形下方区域

用Manim实现——计算和绘制图形下方区域 get_area 函数 get_area是一个用于计算和绘制图形下方区域的函数&#xff0c;常用于图形动画库&#xff08;如 Manim&#xff09; get_area(graph, x_rangeNone, color(ManimColor(#58C4DD),ManimColor(#83C167)), opacity0.3, bounde…

市场主流 AI 视频生成技术的迭代路径

AI视频生成技术的迭代路径经历了从GANVAE、Transformer、Diffusion Model到Sora采用的DiT架构&#xff08;TransformerDiffusion&#xff09;等多个阶段&#xff0c;每个阶段的技术升级都在视频处理质量上带来了飞跃性的提升。这些技术进步不仅推动了AI视频生成领域的快速发展&…

大载重无人机必备:适航证技术详解

随着无人机技术的飞速发展&#xff0c;大载重无人机在物流运输、农业植保、应急救援等领域展现出巨大潜力。然而&#xff0c;为确保这些无人机在空中运行的安全性与高效性&#xff0c;获取适航证成为不可或缺的关键步骤。本文将深入探讨大载重无人机适航证的必备要素&#xff0…

用phpstudy搭建MySQL数据库

使用环境&#xff1a;win11 使用软件&#xff1a;phpstudy 下载地址&#xff1a;小皮面板(phpstudy) - 让天下没有难配的服务器环境&#xff01; MySQL数据库搭建步骤&#xff1a; 1、在小皮的设置界面检测 3306 端口&#xff0c;保障 3306 端口可用&#xff1b; 2、在小皮…

42 PCB布线叠层与阻抗介绍43 PCB布线过孔添加与设置44 差分对添加与设置45 布线间距规则与介绍

42 PCB布线叠层与阻抗介绍&&43 PCB布线过孔添加与设置&44 差分对添加与设置&&45 布线间距规则与介绍 第一部分 42 PCB布线叠层与阻抗介绍1 板子是怎么来的。2 四层板为例&#xff0c;做叠层和阻抗计算。 第二部分 43 PCB布线过孔添加与设置介绍PCBEdotor中…

STM32F28335实验:蜂鸣器实验

实验三个文档&#xff1a; 蜂鸣器驱动&#xff1a; 1.5KHZ-5KHZ之间&#xff0c;我们取5KHZ 也就是高低点平各100um&#xff0c;周期为200um的方波 LED1还是GPIO68管脚 蜂鸣器管脚GPIO6 蜂鸣器驱动&#xff1a; beep.c /** heep.c** Created on: 2024年8月4日* Au…

Studying-代码随想录训练营day54| 110.字符串接龙、105.有向图的完全可达性、106.岛屿的周长

第53天&#xff0c;图论04&#xff0c;加强广搜和深搜的理解练习&#x1f4aa;(ง •_•)ง&#xff0c;编程语言&#xff1a;C 目录 110.字符串接龙 105.有向图的完全可达性 106.岛屿的周长 总结 110.字符串接龙 文档讲解&#xff1a;手撕字符串接龙 题目&#xff1a;110…

黑马Java零基础视频教程精华部分_12_面向对象进阶(4)_内部类

《黑马Java零基础视频教程精华部分》系列文章目录 黑马Java零基础视频教程精华部分_1_JDK、JRE、字面量、JAVA运算符 黑马Java零基础视频教程精华部分_2_顺序结构、分支结构、循环结构 黑马Java零基础视频教程精华部分_3_无限循环、跳转控制语句、数组、方法 黑马Java零基础视…

【九】Hadoop3.3.4HA高可用配置

文章目录 1.高可用基本原理1.NameNode 高可用性主备 NameNodeJournalNode 2.Zookeeper 协调3.Quorum Journal Manager (QJM)4.Failover 控制器5.元数据共享6.检查点机制7.切换过程 2.Hadoop高可用配置1.环境背景2.hdfs-site.xml基本配置高可用配置 3.core-site.xml基本配置代理…

【C++】模版:范式编程、函数模板、类模板

目录 一.范式编程 二.函数模板 1.概念与格式 2.原理 3.实例化 4.匹配规则 三.类模板 一.范式编程 在写C函数重载的时候&#xff0c;可能会写很多同一类的函数&#xff0c;例如交换函数&#xff1a; void Swap(int& left, int& right) {int temp left;left r…