当前位置: 首页 > news >正文

C++中的多态

【1】表现形式:同样的调用语句有多种不同的表现形态

【2】分类:静态联编和动态联编

静态联编有函数重载(运算符重载是特殊的函数重载),模板

【3】重点说下动态联编

【3.1】动态联编的实现需要以下步骤:

  1. 有继承关系、
  2. 父类函数有virtual关键字
  3. 子类对父类对虚函数进行重写
  4. 父类的指针(引用)指向子类对象
  5. 通过父类的指针调用虚方法触发多态

【3.2】动态联编的编译器实现原理

当类中声明虚函数时,编译器会在类中生成一个虚函数表

虚函数表是一个存储类成员函数指针的数据结构

虚函数表是由编译器自动生成与维护的

virtual成员函数会被编译器放入虚函数表中 

存在虚函数时,够造对象时,对象中都有一个指向虚函数表的指针(vfptr指针) 

Vfptr   virtual function pointer 

如我们有下面的这个函数:

Class parent

{

Public:

Virtual func();

}

Class son:public parent

{

Public:

Virtual func();

}

void run(parent*p)

{

p->func();

}

编译器来确定func是否为虚函数

a.如果不是虚函数,编译器可以直接确定调用的函数,静态联编,根据parent类型来确定,编译完成之后就知道调用哪个函数地址了。

b.如果是虚函数,编译器根据对象p的vptr指针,所指的虚函数表中查找func函数,并调用,查找和调用时在执行时完成,动态联编。

说明1:

通过虚函数表指针VFPTR调用重写函数是在程序运行时进行的,因此需要通过寻址操作才能确定真正应该调用的函数。而普通成员函数是在编译时就确定了调用的函数。在效率上,虚函数的效率要低很多。

说明2:

出于效率考虑,没有必要将所有成员函数都声明为虚函数

这里先给出一个图片来简单表示下,单继承的情况下,虚函数表的由来:

再给出一个证明虚函数的例子,以及探究虚函数表内部机构的例子:

参考链接:C++ 虚函数表解析---陈皓改进版_啊大1号的博客-CSDN博客_虚表 陈皓

#include<iostream>
using namespace std;
class Base {public:virtual void f() { cout << "Base::f" << endl; }virtual void g() { cout << "Base::g" << endl; }virtual void h() { cout << "Base::h" << endl; }
};typedef void(*Fun)(void);/*事实上,楼主写的一开始就是错的,虚函数表是类对象之间共享的,* 而非每个对象都保存了一份,楼主得到的也只是虚指针的地址,* 而非虚函数表的地址,事实上对虚函数指针的地址解引用得到的才* 是虚函数表的地址(因为虚函数指针指向虚函数表),以上经过理论和实际验证。
MyClass mc;//奇技淫巧、用int*只是因为32位系统中指针大小跟int相同//  pFun = (Fun)*( (int*)*(int*)(&mc)+1);//跟下面一样auto a1 = (int*)&mc;//找到虚指针的位置(地址)auto a2 = *a1;//得到虚指针的内容(指向的虚表的地址)auto a3 = (int*)a2;//得到虚表的地址pFun = (Fun)*(a3+1);//偏移得到虚表中某个虚函数的地址,解引用得到函数本身pFun();* */int main(){//虚函数表是用这个数组实现的 现在已经确定了cout<<sizeof(int)<<sizeof(long long)<<endl;Base b;Fun pFun = NULL;cout << "虚函数(表)指针 地址:" << (long long *)(&b) << endl;cout << "虚函数表 — 的地址:" << (long long*)*(long long *)(&b) << endl;
// Invoke the first virtual function// 这里才得到第一个虚函数的地址pFun = (Fun)*((long long *)*(long long *)(&b)+0);pFun();pFun = (Fun)*((long long *)*(long long *)(&b)+1);pFun();pFun = (Fun)*((long long *)*(long long *)(&b)+2);pFun();
}

这个时候你应该懂了吧。什么?还是有点晕。也是,这样的代码看着太乱了。没问题,让我画个图解释一下。如下所示:

注意:在上面这个图中,我在虚函数表的最后多加了一个结点,这是虚函数表的结束结点,就像字符串的结束符'\0'一样,其标志了虚函数表的结束。这个结束标志的值在不同的编译器下是不同的。在WinXP+VS2003下,这个值是NULL。而在Ubuntu 7.10 + Linux 2.6.22 + GCC 4.1.3下,这个值如果是1,表示还有下一个虚函数表,如果值是0,表示是最后一个虚函数表。

下面,我将分别说明“无覆盖”和“有覆盖”时的虚函数表的样子。没有覆盖父类的虚函数是毫无意义的。我之所以要讲述没有覆盖的情况,主要目的是为了给一个对比。在比较之下,我们可以更加清楚地知道其内部的具体实现。

一般继承(无虚函数覆盖)

下面,再让我们来看看继承时的虚函数表是什么样的。假设有如下所示的一个继承关系:

请注意,在这个继承关系中,子类没有重载任何父类的函数。那么,在派生类的实例中,其虚函数表如下所示:

对于实例:Derive d; 的虚函数表如下:

我们可以看到下面几点:

1)虚函数按照其声明顺序放于表中。

2)父类的虚函数在子类的虚函数前面。

我相信聪明的你一定可以参考前面的那个程序,来编写一段程序来验证。

 一般继承(有虚函数覆盖)

 覆盖父类的虚函数是很显然的事情,不然,虚函数就变得毫无意义。下面,我们来看一下,如果子类中有虚函数重载了父类的虚函数,会是一个什么样子?假设,我们有下面这样的一个继承关系。

为了让大家看到被继承过后的效果,在这个类的设计中,我只覆盖了父类的一个函数:f()。那么,对于派生类的实例,其虚函数表会是下面的一个样子:

 

我们从表中可以看到下面几点,

1)覆盖的f()函数被放到了虚表中原来父类虚函数的位置。

2)没有被覆盖的函数依旧。

这样,我们就可以看到对于下面这样的程序,

    Base *b = new Derive();b->f();

由b所指的内存中的虚函数表的f()的位置已经被Derive::f()函数地址所取代,于是在实际调用发生时,是Derive::f()被调用了。这就实现了多态。

 

 

 

 

相关文章:

C++中的多态

【1】表现形式&#xff1a;同样的调用语句有多种不同的表现形态 【2】分类&#xff1a;静态联编和动态联编 静态联编有函数重载(运算符重载是特殊的函数重载),模板 【3】重点说下动态联编 【3.1】动态联编的实现需要以下步骤&#xff1a; 有继承关系、父类函数有virtual关…...

Swift如何保证线程安全

Swift可以通过以下几种方式来保证线程安全 使用互斥锁&#xff08;Mutex&#xff09;&#xff1a;使用互斥锁可以防止多个线程同时访问共享数据&#xff0c;保证线程安全。 使用OSAtomic操作&#xff1a;OSAtomic操作可以在多线程环境中安全地执行原子操作。 使用DispatchQue…...

整型提升+算术转换——“C”

各位CSDN的uu们你们好呀&#xff0c;今天小雅兰的内容是之前操作符那篇博客中没有讲完的内容&#xff0c;整型提升这个小知识点也非常重要&#xff0c;那现在&#xff0c;就让我们进入操作符的世界吧 隐式类型转换 算术转换 操作符的属性 隐式类型转换 表达式求值的顺序一部…...

Freemarker介绍

2. Freemarker介绍 FreeMarker 是一个用 Java 语言编写的模板引擎&#xff0c;它基于模板来生成文本输出。FreeMarker与 Web 容器无关&#xff0c;即在 Web 运行时&#xff0c;它并不知道 Servlet 或 HTTP。它不仅可以用作表现层的实现技术&#xff0c;而且还可以用于生成 XML…...

【软件测试开发】Junit5单元测试框架

目录1. 注解Test 注解BeforeEach BeforeAllAfterEach AfterAll2. 断言 assertassertequalsassertTrue assertFalseassertNull assertNotNull3. 用例执行顺序方法排序&#xff0c;通过 Order 注解来排序4. 测试套件 Suite5. 参数化单参数stringsints6. 参数化多参数CsvSourceCsv…...

【C语言技能树】程序环境和预处理

Halo&#xff0c;这里是Ppeua。平时主要更新C语言&#xff0c;C&#xff0c;数据结构算法......感兴趣就关注我吧&#xff01;你定不会失望。 &#x1f308;个人主页&#xff1a;主页链接 &#x1f308;算法专栏&#xff1a;专栏链接 我会一直往里填充内容哒&#xff01; &…...

数据库的三大范式

1.为什么需要数据库设计 设计数据表的时候&#xff0c;要考虑很多的问题: 用户需要哪些数据&#xff0c;我们在数据表中要保存哪一些数据怎么保证数据表中的数据的正确性如何降低数据表的冗余度开发人员怎么才能更方便的使用数据库 如果数据库设计得不合理的话&#xff0c;可…...

【MT7628】开发环境搭建-Fedora12安装之后无法上网问题解决

1.按照如下图所示,打开Network Connections 2.点击Network Connections,弹出如下界面...

[Android Studio]Android 数据存储-文件存储学习笔记-结合保存QQ账户与密码存储到指定文件中的演练

&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea; Android Debug&#x1f7e7;&#x1f7e8;&#x1f7e9;&#x1f7e6;&#x1f7ea; Topic 发布安卓学习过程中遇到问题解决过程&#xff0c;希望我的解决方案可以对小伙伴们有帮助。 &#x1f4cb;笔记目…...

【openGauss实战9】深度分析分区表

&#x1f4e2;&#x1f4e2;&#x1f4e2;&#x1f4e3;&#x1f4e3;&#x1f4e3; 哈喽&#xff01;大家好&#xff0c;我是【IT邦德】&#xff0c;江湖人称jeames007&#xff0c;10余年DBA及大数据工作经验 一位上进心十足的【大数据领域博主】&#xff01;&#x1f61c;&am…...

XSS跨站脚本攻击剖析与防御:初识XSS

目录 跨站脚本介绍 1. 什么是XSS跨站脚本 2. XSS跨站脚本实例 3. XSS漏洞的危害 XSS的分类 1. 反射型XSS 2. 持久性XSS XSS构造 1. 利用< >标记注射Html /Javascript 2. 利用HTML标签属性值执行XSS 3. 空格回车Tab 4. 对标签属性值转码 5. 产生自己的事件…...

Python 高级编程之网络编程 Socket(六)

文章目录一、概述二、Python socket 模块1&#xff09;Socket 类型1、创建 TCP Socket2、创建 UDP Socket2&#xff09;Socket 函数1、服务端socket函数2、客户端socket函数3、公共socket函数三、单工&#xff0c;半双工以及全双工通信方式的区别四、单工&#xff0c;半双工以及…...

centos学习记录

遇到的问题及其解决办法 centos7安装图形化界面 yum groupinstall ‘X Window System’ yum groupinstall -y ‘GNOME Desktop’ 安装完成后输入init 5进入图形化界面 centos7安装vmware-tools 第一步卸载open-vm-tools 输入命令 yum remove open-vm-tools 输入命令 reboot 在…...

为什么说网络安全是风口行业?

前言 “没有网络安全就没有国家安全”。当前&#xff0c;网络安全已被提升到国家战略的高度&#xff0c;成为影响国家安全、社会稳定至关重要的因素之一。 网络安全行业特点 1、就业薪资非常高&#xff0c;涨薪快 2021年猎聘网发布网络安全行业就业薪资行业最高人均33.77万&…...

12-PHP使用过的函数 111-120

111、rowCount if ($stmt->execute($data)) {//true//读:select//写:insert,update,delete,成功后会返回表中受影响的记录数量//!rowCount() 返回受影响的记录数量if ($stmt->rowCount() > 0) {echo 新增成功,id . $db->lastInsertId() . <hr>;} else {//…...

【JavaWeb项目】简单搭建一个前端的博客系统

博客系统项目 本项目主要分成四个页面: 博客列表页博客详情页登录页面博客编辑页 该系统公共的CSS样式 common.css /* 放置一些各个页面都会用到的公共样式 */* {margin: 0;padding: 0;box-sizing: 0; }/* 给整个页面加上背景 */ html, body{height: 100%; }body {backgrou…...

iPerf3 -M参数详解,场景分析

本文目录iPerf3 -M参数说明几个典型测试场景中应该如何设定合适的-M参数值理想局域网模型&#xff08;无丢包&#xff0c;无抖动&#xff09;高丢包&#xff0c;无抖动模型高丢包&#xff0c;高抖动模型&#xff08;网络质量比较差&#xff0c;IP转发路径变化频繁&#xff09;总…...

java的基本语法以及注意事项

Java 基础语法一个 Java 程序可以认为是一系列对象的集合&#xff0c;而这些对象通过调用彼此的方法来协同工作。下面简要介绍下类、对象、方法和实例变量的概念。对象&#xff1a;对象是类的一个实例&#xff0c;有状态和行为。例如&#xff0c;一条狗是一个对象&#xff0c;它…...

matlab搭建IAE,ISE,ITAE性能指标

目录前言准备IAEISEITAE前言 最近在使用matlab搭建控制系统性能评价指标模型&#xff0c;记录一下 准备 MATLAB R2020 IAE IAE函数表达式如下所示&#xff1a; IAE函数模型如下所示&#xff1a; ISE ISE函数表达式如下所示&#xff1a; ISE函数模型如下所示&#xff…...

docker安装mysql

在安装Mysql之前&#xff0c;我们可以先查看一下我们的镜像&#xff0c;输入命令&#xff1a; docker images 能发现&#xff0c;镜像里面只有一个Nginx&#xff0c;并没有Mysql 然后我们可以像上一篇安装Nginx一样&#xff0c;安装Mysql镜像。 输入以下命令&#xff0c;安装…...

CTF show Web 红包题第六弹

提示 1.不是SQL注入 2.需要找关键源码 思路 进入页面发现是一个登录框&#xff0c;很难让人不联想到SQL注入&#xff0c;但提示都说了不是SQL注入&#xff0c;所以就不往这方面想了 ​ 先查看一下网页源码&#xff0c;发现一段JavaScript代码&#xff0c;有一个关键类ctfs…...

如何在看板中体现优先级变化

在看板中有效体现优先级变化的关键措施包括&#xff1a;采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中&#xff0c;设置任务排序规则尤其重要&#xff0c;因为它让看板视觉上直观地体…...

Keil 中设置 STM32 Flash 和 RAM 地址详解

文章目录 Keil 中设置 STM32 Flash 和 RAM 地址详解一、Flash 和 RAM 配置界面(Target 选项卡)1. IROM1(用于配置 Flash)2. IRAM1(用于配置 RAM)二、链接器设置界面(Linker 选项卡)1. 勾选“Use Memory Layout from Target Dialog”2. 查看链接器参数(如果没有勾选上面…...

Python爬虫(一):爬虫伪装

一、网站防爬机制概述 在当今互联网环境中&#xff0c;具有一定规模或盈利性质的网站几乎都实施了各种防爬措施。这些措施主要分为两大类&#xff1a; 身份验证机制&#xff1a;直接将未经授权的爬虫阻挡在外反爬技术体系&#xff1a;通过各种技术手段增加爬虫获取数据的难度…...

python报错No module named ‘tensorflow.keras‘

是由于不同版本的tensorflow下的keras所在的路径不同&#xff0c;结合所安装的tensorflow的目录结构修改from语句即可。 原语句&#xff1a; from tensorflow.keras.layers import Conv1D, MaxPooling1D, LSTM, Dense 修改后&#xff1a; from tensorflow.python.keras.lay…...

#Uniapp篇:chrome调试unapp适配

chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器&#xff1a;Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

Redis:现代应用开发的高效内存数据存储利器

一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发&#xff0c;其初衷是为了满足他自己的一个项目需求&#xff0c;即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源&#xff0c;Redis凭借其简单易用、…...

0x-3-Oracle 23 ai-sqlcl 25.1 集成安装-配置和优化

是不是受够了安装了oracle database之后sqlplus的简陋&#xff0c;无法删除无法上下翻页的苦恼。 可以安装readline和rlwrap插件的话&#xff0c;配置.bahs_profile后也能解决上下翻页这些&#xff0c;但是很多生产环境无法安装rpm包。 oracle提供了sqlcl免费许可&#xff0c…...

java高级——高阶函数、如何定义一个函数式接口类似stream流的filter

java高级——高阶函数、stream流 前情提要文章介绍一、函数伊始1.1 合格的函数1.2 有形的函数2. 函数对象2.1 函数对象——行为参数化2.2 函数对象——延迟执行 二、 函数编程语法1. 函数对象表现形式1.1 Lambda表达式1.2 方法引用&#xff08;Math::max&#xff09; 2 函数接口…...

《Offer来了:Java面试核心知识点精讲》大纲

文章目录 一、《Offer来了:Java面试核心知识点精讲》的典型大纲框架Java基础并发编程JVM原理数据库与缓存分布式架构系统设计二、《Offer来了:Java面试核心知识点精讲(原理篇)》技术文章大纲核心主题:Java基础原理与面试高频考点Java虚拟机(JVM)原理Java并发编程原理Jav…...