多态深度剖析

前言

继承是多态的基础,

如果对于继承的知识还不够了解,

可以去阅读上一篇文章

继承深度剖析

基本概念与定义

概念:

通俗来说,就是多种形态。具体点就是去完成某个行为,

当不同的对象去完成时会产生出不同的状态。

举个栗子:

比如买票这个行为,当普通人买票时,是全价买票;

学生买票时,是半价买票;

军人买票时是优先买票。

构成多态的两个必要条件:

1. 必须通过基类的指针或者引用调用虚函数
2. 被调用的函数必须是虚函数,且派生类必须对基类的虚函数进行重写

那么,什么是虚函数?什么是重写?

将virtual关键字加在成员函数前面,这个函数就是虚函数

虚函数的重写(覆盖)

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

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

实例

使用多态时,切记要注意构成多态的两个必要条件

实例

class Person
{
public:virtual void Buy(){cout << "全价" << endl;}
};class student :public Person
{
public:virtual void Buy(){cout << "半价" << endl;}
};int main()
{Person p;student s;p.Buy();s.Buy();return 0;
}

运行结果

p 是基类 Person 的实例,s 是派生类 Student 的实例。

当 p 调用 Buy 函数时,它调用的是 Person 类中的函数,

因此输出 “全价”。而当 s 调用 Buy 函数时,

它调用的是 Student 类中的重写函数,因此输出 “半价”。

通过重写基类中的虚函数,派生类可以改变函数的行为,这就是多态性。

尽管 p 和 s 都调用了 Buy 函数,但由于它们所属的类不同,输出的结果也不同。

构成多态的两个特例

1.派生类虚函数不写virtual关键字依旧构成多态

2.基类与派生类虚函数返回值类型不同
也可以构成多态(返回值必须满足某种条件)

基类的返回值要返回基类
派生类的返回值要返回派生类

注意

1.父类不写virtual,而子类的同名
函数写了virtual,这是不构成多态的!

class Person
{
public:void Buy(){}
};class student :public Person
{
public:virtual void Buy(){}
};

2.在继承体系中,父子类的同名
函数不构成重写就构成隐藏,不可能构成重载!

底层原理分析

大家先思考一下这套题目的答案,

如果你单纯的认为Base类只有一个
整型变量占用空间,答案是4的话,那你就上当啦!
事实上在32位机器下,这里的结果是8
在64位机器下,这里的结果是16!

32

64

因为它除了有一个变量外,还有
一个指针,此指针指向一个虚函数表

使用这一段代码观察

class A
{
public:virtual void func1(){cout << "父类func1";}
private:int _a;
};
class B : public A
{
public:virtual void func1(){cout << "子类func1";}
private:int _b;
};int main()
{A a;B b;return 0;
}

此指针叫虚表指针:vfptr,也就是
virtual function ptr

这个指针并不是直接指向虚函数的地址
而是指向一个虚函数表,可以理解位一个
数组,此数组中存放着此对象中所有的虚
函数的地址,它们的关系可以用下图表示:

注:不管有没有继承体系或多态
只要有虚函数就有虚表!

那么父类和子类的虚表指针和指向
的内容有什么不同或相同处吗?
形成多态现象的原理又是什么?

class A
{
public:virtual void func1()cout << "父类func1";virtual void func2()cout << "父类func2";
private:int _a;
};
class B : public A
{
public:virtual void func1()cout << "子类func1";
private:int _b;
};
int main()
{A a;B b;return 0;
}

这证明:

父类和子类的虚表指针是不同的
证明父子类各有一张虚函数表!
函数func1在子类中被重写了,所以
父子类虚表中的func1函数地址是不同的
函数func2没有被子类重写,所以
父子类虚表中的func2函数地址是相同的

结论:同一个类的不同对象共用一个虚表

多态的原理深度剖析:

当一个函数A被重写时,它的父类虚表存放
父类函数A的地址,子类虚表存放的是子类
函数A的地址!

当父类的指针或引用指向子类空间时
调用虚函数时,会到指向对象的虚表中
中找到对应的虚函数地址,进行调用!

父子类都只有A函数或无函数时

  1. 若父类写了虚函数A,而子类
    甚至没有写函数A,此时子类对象中
    存储的虚函数地址与父类相同

  2. 若父类甚至没有写函数A,而子类
    直接写了虚函数A,则父类对象中没有
    虚表,而子类对象中有虚表(存放A)

多态中的两个关键字

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

override:检查子类类虚函数是否重写了
基类虚函数如果没有被重写则编译报错

 抽象类以及虚函数的几个结论

抽象类概念:

在虚函数的后面写上 =0 ,则这个函数为纯虚函数。包含纯虚函数的类叫做抽象类(也叫接口类),抽象类不能实例化出对象。派生类继承后也不能实例化出对象,只有重写纯虚函数,派生类才能实例化出对象。纯虚函数规范了派生类必须重写

抽象类的只需了解即可,实际中使用到的场景很少

其他结论

1.内联函数可以是虚函数吗?

可以,如果是普通调用,内联起作用,如果是多态调用,内联不起作用。

2.静态成员可以是虚函数吗?

不可以,编译会报错,静态成员函数没有this指针,可以指定类域调用,无法构成多态。

3.构造函数可以是虚函数吗?

不可以,编译会报错,对象中的虚表指针是构造函数阶段时才初始化的,虚函数多态调用,要到虚表中找,但是此时虚表指针还未初始化。

4.析构函数可以是虚函数吗?

最好是虚函数。

5.访问普通函数快还是访问虚函数快?
普通调用时是一样快的,多态调用时会慢一点,以为要去虚表中查找。

6.虚函数表在什么阶段形成,存在哪里?

虚函数表在编译阶段就形成了,虚函数表指针构造时才初始化给对象,存储在代码段中。

7.动态多态与静态多态

静态多态多指函数重载,运算符重载;

动态多态就是本章的内容了,

条件:1.父类的引用或指针调用虚函数。

2.虚函数完成重写指向谁,调用谁,实现多种形态

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

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

相关文章

docker使用auth登录

配置config.json文件 vim ~/.docker/config.json 格式&#xff1a; {"auths":{"registry.digitalocean.com":{"auth":"xxx"}} }QA:配置不生效&#xff0c;去掉默认的一些其他配置&#xff0c;只保留auth部分 直接使用auth登录 docke…

淘宝商品信息一网打尽!深入探索item_get接口的高效应用

淘宝item_get接口技术详解 一、引言 淘宝的开放平台为开发者提供了丰富的API接口&#xff0c;其中item_get接口是用于获取淘宝商品详情的常用接口。通过这个接口&#xff0c;开发者可以获取到商品的标题、价格、描述、图片等关键信息&#xff0c;进而实现商品数据的抓取、分析…

如何完美解决 Xshell 使用 SSH 连接 Linux 服务器报错:找不到匹配的 host key 算法

&#x1f6e0;️ 如何完美解决 Xshell 使用 SSH 连接 Linux 服务器报错&#xff1a;找不到匹配的 host key 算法 摘要&#xff1a; 本文将带领大家深入学习如何解决 Xshell 使用 SSH 连接 Linux 服务器时报错“找不到匹配的 host key 算法”的问题。通过详细的操作步骤和代码案…

第12章:集合框架

第12章&#xff1a;随堂复习与企业真题&#xff08;集合框架&#xff09; 一、随堂复习 1. 数组存储数据方面的特点和弊端 数组存储多个数据方面的特点&#xff1a;> 数组一旦初始化&#xff0c;其长度就是确定的。> 数组中的多个元素是依次紧密排列的&#xff0c;有序的…

05-5.4.3 树和森林的遍历

&#x1f44b; Hi, I’m Beast Cheng &#x1f440; I’m interested in photography, hiking, landscape… &#x1f331; I’m currently learning python, javascript, kotlin… &#x1f4eb; How to reach me --> 458290771qq.com 喜欢《数据结构》部分笔记的小伙伴可以…

Shopee虾皮API:获取商家店铺商品列表

一、平台介绍 Shopee&#xff0c;作为东南亚及中国台湾地区领先的电商平台&#xff0c;为卖家提供了一个便捷、高效的销售渠道。作为卖家&#xff0c;能够将自己的商品展示在Shopee平台上&#xff0c;并通过平台的流量和工具&#xff0c;将商品销售给更多的潜在买家。 为了帮…

系统架构设计师 - 操作系统(2)

操作系统 操作系统&#xff08;5-6分&#xff09;文件管理绝对路径与相对路径 ★索引文件 ★★★位示图 ★ 存储管理段页式存储 ★★★页式存储段式存储段页式存储&#xff08;了解&#xff09; 页面置换算法 ★ 微内核操作系统嵌入式操作系统 ★★★ 大家好呀&#xff01;我是…

数据库课设-中小企业工资管理系统

一、效果展示 二、后端代码 import string import random from flask import Flask, render_template, request, jsonify, redirect, session import pymysql from flask_cors import CORS import time import schedule from datetime import datetime import threading from …

HK1-BOX X3刷UBUNTU 24.04,并开启WIFI

端午刚好有点时间&#xff0c;顺便把改完散热的HK1-BOX刷了个最新OC版的UBUNTU 24&#xff0c;这里记录下操作的步骤&#xff1a; 准备材料 HK1-BOX S905X3&#xff1a;注意X4的不行固件没匹配的。建议先改完散热&#xff0c;不然作为7X24小时的机器长时间高温还是很伤硬件的…

Web前端项目-拼图游戏【附源码】

拼图游戏 拼图游戏是一种经典的益智游戏&#xff0c;通过HTML、CSS和JavaScript等前端技术的综合运用来实现&#xff1b;拼图游戏可以锻炼玩家的观察能力、空间认知能力和逻辑思维能力。游戏开始时&#xff0c;一张图片会被切割成多个小块&#xff0c;并以随机顺序排列在游戏区…

CMOS图像传感器——列噪声(CFPN)去除

目前CMOS 图像传感器系统中列共用结构应用最为广泛,在该结构中,虽然像素曝光均匀,但是由于列输出系统处理属性的变动,对于不同列,像素的输出是不均匀的。因此,基于列的CMOS 图像传感器表现出垂直条纹固定模式噪声(CFPN),从而降低了图像的质量。由于人眼 的关系,CFPN …

「布道师系列文章」宝兰德徐清康解析 Kafka 和 AutoMQ 的监控

作者&#xff5c;北京宝兰德公司解决方案总监徐清康 01 前言 当我们使用一个软件的时候&#xff0c;经常都会问这个软件怎么监控、监控他的哪些指标&#xff1f;Kafka 的监控挺长时间都是一个老大难的问题&#xff0c;社区在监控方面一直没有投入太大的精力。如果要实现一…

kotlin 中的字符串

一、字符类访问 1、字符串的访问跟js一样&#xff0c;可以使用索引来访问或者直接循环。 fun main() {val a: String "2024"// 方式一&#xff1a;for (item in a) {println(item) // 输出每一个字符}// 方式二&#xff1a;println("${a[0]}, ${a[1]}, ${a[2…

Leetcode 415. 字符串相加-大数相加

415. 字符串相加 - 力扣&#xff08;LeetCode&#xff09; class Solution {/**2024.6.17大数相加&#xff0c;从2个字符串最后一位开始加&#xff0c;如果没遍历到下标0&#xff0c;就一直遍历&#xff0c;减去‘a’得到数值&#xff0c;循环结束条件就是 字符串1遍历完了&am…

IAP固件升级进阶(Qt上位机)

前言 时隔近一年&#xff0c;再次接触IAP固件升级&#xff0c;这次修改了以前的一些bug&#xff0c;同时新增一些实用性的功能。 有纰漏请指出&#xff0c;转载请说明。 学习交流请发邮件 1280253714qq.com。 上位机界面 视频演示 当Up对iap固件升级的机制有了更深的理解后…

【C++】类和对象的引入

文章目录 前言一、类的定义二、类的访问控制与封装三、类的作用域四、类的实例化五、类的存储方式及大小计算六、隐藏的this指针 前言 C语言是面向过程的&#xff0c;关注的是过程&#xff0c;分析出求解问题的步骤&#xff0c;通过函数调用逐步解决问题。 C是基于面向对象的&…

Git与SSH

Git Git是一种分布式版本控制系统&#xff0c;最初由Linus Torvalds为管理Linux内核开发而设计并开发。Git可以帮助开发团队协作管理代码&#xff0c;跟踪代码变更历史&#xff0c;并在需要时回溯到特定版本。 分布式版本控制&#xff1a;每个开发者都可以拥有完整的代码仓库…

go使用ssh执行脚本文件

目的&#xff1a;在go语言中ssh连接服务器&#xff0c;上传shell文件并执行 代码&#xff1a; import "golang.org/x/crypto/ssh" func InitHardware(username string, password string, ip string, ipv6 string, port int, arch string, ipStack string, classTy…

算法题解记录29+++全排列(百日筑基)

一、题目描述 题目难度&#xff1a;中等 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],[1,3,2],[2,1,3],[2,3,1],[3,1,2],[3,2,1]] 示…

华为网络设备高频命令

1.system-view • 用法&#xff1a;在用户视图下执行 system-view 命令。 • 作用&#xff1a;进入系统视图&#xff0c;以便进行配置性的操作。 • 场景&#xff1a;当需要对设备进行系统级的配置时。 2.sysname XXX • 用法&#xff1a;执行 [Huawei]sysname XXX 命令。…