通过对象指针的方式强行指定到子类_C++中的虚指针与虚函数表

1a80b31c3c3390412a3ca1c34e680928.png

​ 最近在逛B站的时候发现有候捷老师的课程,如获至宝。因此,跟随他的讲解又复习了一遍关于C++的内容,收获也非常的大,对于某些模糊的概念及遗忘的内容又有了更深的认识。

以下内容是关于虚函数表、虚函数指针,而C++中的动态绑定实现和这两个内容是分不开的。

虚函数表、虚指针

​ 当一个类在实现的时候,如果存在一个或以上的虚函数时,那么这个类便会包含一张虚函数表。而当一个子类继承并重载了基类的虚函数时,它也会有自己的一张虚函数表。当我们在设计类的时候,如果把某个函数设置成虚函数时,也就表明我们希望子类在继承的时候能够有自己的实现方式;如果我们明确这个类不会被继承,那么就不应该有虚函数的出现。

下面是某个基类A的实现:

class 

​ 从下图中可以看到该类在内存中的存放形式,对于虚函数的调用是通过查虚函数表来进行的,每个虚函数在虚函数表中都存放着自己的一个地址,而如何在虚函数表中进行查找,则是通过虚指针来调用,在内存结构中它一般都会放在类最开始的地方,而对于普通函数则不需要通过查表操作。这张虚函数表是什么时候被创建的呢?它是在编译的时候产生,否则这个类的结构信息中也不会插入虚指针的地址信息。

eef44b74941216743cf9456f5b50afe5.png

以下例子包含了继承关系:

class 

以上三个类在内存中的排布关系如下图所示:

780deecb9ee2a08dee6764e78a8609df.png
  1. 对于非虚函数,三个类中虽然都有一个叫 func2 的函数,但他们彼此互不关联,因此都是各自独立的,不存在重载一说,在调用的时候也不需要进行查表的操作,直接调用即可。
  2. 由于子类B和子类C都是继承于基类A,因此他们都会存在一个虚指针用于指向虚函数表。注意,假如子类B和子类C中不存在虚函数,那么这时他们将共用基类A的一张虚函数表,在B和C中用虚指针指向该虚函数表即可。但是,上面的代码设计时子类B和子类C中都有一个虚函数 vfunc1,因此他们就需要各自产生一张虚函数表,并用各自的虚指针指向该表。由于子类B和子类C都对 vfunc1 作了重载,因此他们有三种不同的实现方式,函数地址也不尽相同,在使用的时候需要从各自类的虚函数表中去查找对应的 vfunc1 地址。
  3. 对于虚函数 vfunc2,两个子类都没有进行重载操作,所以基类A、子类B和子类C将共用一个 vfunc2,该虚函数的地址会分别保存在三个类的虚函数表中,但他们的地址是相同的。
  4. 从上图可以发现,在类对象的头部存放着一个虚指针,该虚指针指向了各自类所维护的虚函数表,再通过查找虚函数表中的地址来找到对应的虚函数。
  5. 对于类中的数据而言,子类中都会包含父类的信息。如上例中的子类C,它自己拥有一个变量 m_data1,似乎是和基类中的 m_data1 重名了,但其实他们并不存在联系,从存放的位置便可知晓。

关于动态绑定

在设计了以上三个类之后,我们就要开始对它们进行使用。

int 

​ 假如在程序中分别创建两个对象 a 和 b,a的创建是通过将b强制转化为类A得来的。对于 b.vfunc1() 的调用,应该没有太的疑问,它所调用的就是类B中的 vfunc1。而对于 a.vfunc1() 的调用,它虽然是强制转化后的结果,但并不能改变它是一个类A对象的事实,因此这里调用的便是类A中的 vfunc1,也就是上图中显示绿色的函数。

int 

​ 将程序改写成以上内容,pa 是一个类A的指针,但它指向的是一个类B的对象。在使用pa调用 vfunc1 的时候,程序发现pa是一个指针,并且现在正在调用一个虚函数叫做 vfunc1,这时通过 pa->vptr 这个虚指针到类B的虚函数中(上图的B vtbl)找对应的虚函数地址,找到该地址以后,就用相应的虚函数来进行调用,也就是调用上图所示的 B::vfunc1()

pa是类A的指针,为什么查找的是类B的虚函数表?
只要某一个类X包含虚函数,无论是它的父类或者它本身拥有,那么这个类的对象都会包含一个虚指针vptr,至于vptr要指向哪张表,取决于类X它本身是否含有虚函数。此处,类B中存在虚函数,那么它就会拥有自己的一张虚函数表。pa指向的是一个类B的对象,因此 p-vptr 指代的是类B中虚指针,所以它查找的是类B的虚函数表
如何从虚函数表中查找到 vfunc1 的地址?
虚函数表中的内容是在编译的时候确定的,通过以下方式进行查找 (* p->vptr[n] )(p) 或者 (* (p->vptr)[n] )(p),它的解读是:通过类对象指针p找到虚指针vptr,再查找到虚函数表中的第n个内容,并将他作为函数指针进行调用,调用时的入参是p(式子中的第二个p),而这个p就是隐藏的this指针,这里的n也是在编译的时候确定的。
int 

​ 再将程序修改成以上内容,对于 p2->vfunc1() 的调用和上文所述一致,它调用的是 B::vfunc1 函数。而对于 p1->vfunc1() 的调用,同样通过上面的方法可知, p1->vptr 它所指向的是类A的虚函数表,因此它调用的是 A::vfunc1 函数。

​ 通过以上内容,我们可以知道在使用基类指针调用虚函数的时候,它能够根据所指的类对象的不同来正确调用虚函数。而这些能够正常工作,得益于虚指针和虚函数表的引入,使得在程序运行期间能够动态调用函数。

动态绑定有以下三项条件要符合:

  1. 使用指针进行调用
  2. 指针属于up-cast后的
  3. 调用的是虚函数

与动态绑定相对应的是静态绑定,它属于编译的时候就确定下来的,如上文的非虚函数,他们是类对象直接可调用的,而不需要任何查表操作,因此调用的速度也快于虚函数。

参考

  1. https://www.bilibili.com/video/av48240648?p=1

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

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

相关文章

datax oracle mysql_从 MySQL 到 Lindorm时序引擎 的数据迁移

背景本文主要介绍如何使用阿里巴巴的开源工具Datax 实现从 MySQL 到 时序引擎 的数据迁移。DataX相关使用介绍请参阅 DataX 的下面将首先介绍 DataX 工具本身,以及本次迁移工作涉及到的两个插件(MySQL Reader 和 TSDB Writer)。DataXDataX 是阿里巴巴集团内被广泛使…

如何手动输入给数组赋值_你是否真的了解VBA数组呢?让我带你认识一下真正的数组...

大家好,我们今日继续讲解VBA代码解决方案的第110讲内容:VBA数组讲解,什么是数组,如何定义数组,如何创建数组一、什么是数组 就是数组共享一个名字,有着多个元素按顺序排列的变量。在数组中,元素…

redhat9安装mysql_redhat 9.0 安装mysql

在官网上下载了MySQL-5.5.9-1.rhel5.i386.tar包 ,将文件以二进制的形式ftp到虚拟机rehat上解压文件到MySQY-5文件夹下:然后将路径切换到解压目录下运行 rpm -ivh *.rpm --force报如下错:rootlocalhost MySQL-5]# rpm -ivh *.rpm --forceerror…

为什么整数在python中表示d_python中整数的缓存机制

在python中,如下代码结果一定不会让你吃惊:Python 3.3.2 (v3.3.2:d047928ae3f6, May 16 2013, 00:06:53) [MSC v.1600 64 bit (AMD64)] on win32Type "copyright", "credits" or "license()" for more information.>&g…

MySQL中序列的作用_MySql中序列的应用和总结

Mysql中的序列主要用于主键,主键是递增的字段,不可重复。Mysql与Oracle不同的是,它不支持原生态的sequence,需要用表和函数的组合来实现类似序列的功能。1.首先创建序列的主表/*Navicat Premium Data TransferSource Server : MyS…

python内置模块重要程度排名_python常用内置模块

#持续更新#在使用内置模块的时候需要导入,例如import abc,则导入abc模块,当然模块也可以自己写,相当于一个类,后面放到类里说,这个因为环境闲置,有些无法执行,只能理解了#os系统操作…

路由器装上去没有网络_5G网络那么快,今后路由器没有存在的价值了?

从一些宣传来看,即将商用的5G网速确实很快,号称10Gbps,比有线宽带还要快。既然5G网速那么快了,还有必要装宽带和路由器吗?先来纠正一个误区,5G网络的理论速度与实际速度是有很大差别的!!目前很多宣传中所说的5G网速最…

python爬取新闻网站内容findall函数爬取_Python爬取新闻网标题、日期、点击量

最近接触Python爬虫,以爬取学校新闻网新闻标题、日期、点击量为例,记录一下工作进度目前,感觉Python爬虫的过程无非两步:Step1.获取网页url(利用Python库函数import urllib2)Step2.利用正则表达式对html中的字符串进行匹配、查找等…

python token发送请求_python发送requests请求时,使用登录的token值,作为下一个接口的请求头信息...

python发送requests请求时,使用登录的token值,作为下一个接口的请求头信息登录接口代码:在这里插入图片描述登陆后返回的结果是:在这里插入图片描述新增渠道接口:新增渠道接口需要在headers中添加登陆后返回的token才可…

python绘制散点图的步骤_python如何绘制散点图?

饼图:饼图显示静态数字以及类别如何表示整体构成的一部分。饼图以百分比表示数字,所有段的总和需要等于100%。plt.pie(df[Age], labels {"A", "B", "C","D", "E", "F","G…

numpy支持比python更多的数据类型_NumPy数据类型

NumPy - 数据类型NumPy 支持比 Python 更多种类的数值类型。 下表显示了 NumPy 中定义的不同标量数据类型。序号数据类型及描述1.bool_存储为一个字节的布尔值(真或假)2.int_默认整数,相当于 C 的long,通常为int32或int643.intc相当于 C 的int&#xff0…

suse11安装python_suse linux 安装python Eric4

1. Install Qt42. Build and install sip3. Build and install QScintilla24. Build and install PyQt45. Build and install QScintilla2 Python bindings6. Install eric4这个文件在eric4-4.4.9/README1.安装python2.62.安装qt4解压 qt-everywhere-opensource-src-4.7.1./con…

coap python3_node-coap入门(三)——Observe

示例调用v0.12.0版本的node-coap,目前只能在server端通过res.end()触发finish事件,client端目前还没实现响应API。但火狐copper插件取消订阅时,server端可以触发finish事件。发现client取消订阅API的读者若能与我交流分享,不甚感激…

怎么把程序内部坐标转为屏幕坐标_经纬度坐标转换成屏幕坐标

地理坐标定义规则:X轴(代表经度)向右递增,Y轴(纬度)向上递增,就好比小学学过的平面坐标。向左、向下的规则。 屏幕坐标定义规则:X轴向右递增,Y轴向下递增。可以看出,地理坐标和屏幕坐标的区别仅仅只是在于Y…

kmeans python_k-means python实践

#codingutf-8import numpy as npimport matplotlib.pyplot as pltfrom sklearn.cluster import KMeansimport sklearn.datasets as dsimport matplotlibmatplotlib.rcParams[font.sans-serif] [uSimHei]matplotlib.rcParams[axes.unicode_minus] False#生成测试数据#生成维度…

mysql max 命令大全_MySQL常用命令,34道练习题(持续更新中)。

1、使用SHOW语句找出在服务器上当前存在什么数据库:mysql> SHOW DATABASES;2、创建一个数据库MYSQLDATAmysql> CREATE DATABASE MYSQLDATA;3、选择你所创建的数据库mysql> USE MYSQLDATA; (按回车键出现Database changed 时说明操作成功!)4、查…

python深度优先_python数据结构之图深度优先和广度优先实例详解

本文实例讲述了python数据结构之图深度优先和广度优先用法。分享给大家供大家参考。具体如下:首先有一个概念:回溯回溯法(探索与回溯法)是一种选优搜索法,按选优条件向前搜索,以达到目标。但当探索到某一步时,发现原先…

mysql sum id 5_mysql怎么使用sum()求id字段的和?

mysql使用sum()求id字段的和的方法:利用“SELECT SUM(id) FROM 数据表名;”来求id字段的和,返回id字段值的总和;SUM()函数在计算时,忽略列值为NULL的行。(推荐教程:mysql视频教程)SUM() 函数返回数值列的总数。SQL SUM…

python问卷调查系统设计案例_从设计到施工,全面剖析超级玻璃屋面系统案例!...

【设计师的材料清单】戳图即可购买《设计师的材料清单》实体书或获取高清电子书转载整理自:弗思特(ID:FFTCCL)各位设计师大家好!随着人们对建筑外观及内部环境要求的不断提高具有大面积采光且轻盈的玻璃屋面系统在大型公共建筑设计中被广泛使…

mysql如何建外建_mysql如何建立外键

mysql如何建立外键一个主表blog博客表,DROP TABLE IF EXISTS blog;CREATE TABLE blog (id int(11) NOT NULL auto_increment,title varchar(11) default NULL,content varchar(11) default NULL,PRIMARY KEY (id)) ENGINEInnoDB DEFAULT CHARSETgb2312;另一个表comment,评论表…