c++对象模型之vptr(虚指针)和vtbl(虚表)浅析

2024-02-01 16:18

本文主要是介绍c++对象模型之vptr(虚指针)和vtbl(虚表)浅析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

  对于学习C++来说,类是一个非常重要的东西,对于面向对象的程序设计,又会设计到类的继承,委托,复合等。对于在类的继承中大多数人都会知道类在完成继承操作时的结果如何,但是如果问他,类在继承的过程中他的内存是如何实现管理的,相信许多人都不一定能够答上来,那么本博文结合我对类的继承过程的了解,给大家简单的说一下,类的继承过程是如何实现的。


  在谈这个话题前,不可避免的我需要先引出vptr(虚指针)和vtbl(虚表)。


  在类的继承中,如下图所示:每一个class产生出一堆指向virtual functions 的指针,放在表格中,而这个表格就是我们所说的vtbl(虚表)……


  对于每一个class object 被添加了一个指针,指向相关的virtual table 。通常这个指针被称为vptr(虚指针)……


这里写图片描述


  下面我们结合上图对vptr和vtbl做进一步的讲解。图中定义了三个类,分别为class A、class B、class C;其中class A 是class B的基类,class B 是class C的基类。


  **class A :**的 公共成员中有四个函数接口,分别为虚函数 virtual void vfunc1(); virtual void vfunc2();普通成员函数void func1();void func2().class A中有两个私有成员,分别为m_data1和m_data2。


  class B :  继承了class A ,但是class B中的虚函数virtual void vfunc1()覆盖了基类的同名虚函数;此外class B的公共成员中还有一个属于自己的函数 void func2(),注意,这里面的函数func2虽然与class A中的函数func2同名,但是他们是互补相关的两个函数,也不存在谁覆盖谁,因为他们并没有将该同名函数申明为virture function(虚函数),所以class B的对象在调用void func2()的时候,它只能调用到class B自身公共成员函数中的void func2()函数,而无法调用到class A 中的void func2()函数。 对由于class B中的私有成员只有一个m_data3.


  **class C 😗*继承了class B,但是但是class C中的虚函数virtual void vfunc1()覆盖了基类class B中的同名虚函数;此外存在一个公共成员函数:void func2(),其调用原理,参考上一段文字(在此处就不做详细说明了)。


  现在我们知道了A、B、C这三个类的继承关系,以及各自所拥有的公有成员、私有成员以及各自的virtual function 。那么我们接下来就来谈谈,各自对象的虚指针和虚表。(如上图的左边部分所示)


  从图的左上角可以看到,当class中有虚函数时,那么他所创建的对象将会多出一个指针(如图中的黑点所示为一个指针),这也是为什么类中有虚函数比类中没有虚函数在进行对象所占字节的测试时,会多出4个字节,而这多出的四个字节其实就是vptr(虚指针);在图中虚指针对应的地址为0x409004,对于对象a的成分除了包括一个vptr(虚指针)外,还包函两个数据成员m_data1、m_data2. 对象a通过虚指针指向虚表(A vtbl),表中放的都是函数指针,指向虚函数。从class A中可以看出class A有两个虚函数,所以vtbl中有两个虚指针,分别指向对应的虚函数。(注意同种相同颜色的框框)。


  此处补充一下:对于图中给出的三个类(class A,class B,class C)他们一共有8个函数。对应的四个普通成员函数四个虚函数,必须要明白的一点是,如果基类中有虚函数,那么子类中一定有虚函数,因为子类继承了基类的成份。并且虚指针只能调用虚函数,而不能调用普通成员函数。


  对于B的对象b,因为class B继承了class A,而class A中有虚函数,那么刚才我们说了继承时,由于子类继承了基类的成分,所以对象b一定也有一个虚指针,指向对应的虚函数。而对象b中由于是继承了基类A,所以对象b的成分按照顺序将会包含:一个虚指针,对象A的m_data1,m_data2(基基类的成份),然后才到自身的m_data3.


  对于C的对象c,他的成分同样是按顺序包括:一个虚指针,类A的m_data1,m_data2,类B的m_data3,和自身的m_data1、m_data4.


  由于是虚指针,各自的虚指针(vptr)只会指向各自对象对应的虚表( vtbl ) ,对于class A的对象a的虚指针指向的虚表有两个虚函数分别为A::vfunc1()和A::vfunc2()。这个比较好理解。


  那么对于class B的对象b,它的虚指针呢。因为class B是class A的子类,子类将会继承基类的成分,而基类有两个虚函数vfunc1()和vfunc2(),这两个虚函数都属于基类的成分,所以继承时都需要继承,但是class B的虚函数virtual void vfunc1()将基类的同名虚函数覆盖掉了,那么实际上对象b只有两个虚函数,分别为自身的虚函数vfunc1(),和来自基类继承的虚函数vfun2()。


  对于class C的对象c而言,其原理可以参考对象b,但是需要说明的一点是,由于class C继承了class B,而class B 又继承了class A,虽然class B
中没有写出虚函数vfunc2(),但是实际上class B中时包含了class A的的虚函数vfunc2()的成分的。又因为class C,继承了class B,这时候我们从上面的图中左下角可以看到,class C的对象中其实上是包含了class B从class A中继承过来的成分。所以此时对于class C的对象c来说,它也是包含了两个虚函数的,分别为自身的虚函数vfunc1(),和A::vfunc2()。


  对于每个类对应的对象的vptr通过vtbl调用的虚函数如图中第二列和第三列之间的箭头所示(在同种可以通过图中相同的颜色进行区分)。


  由于对底层理解能力有限,目前先写这些,后期后进一步了解,我会补充上去。谢谢!


  对于图中的(*(P->vptr)n);大家可能会感到疑惑,我在这里简单说明一下,在图的左下角,大家会看到一个P;那么这行代码的实际意思是:通过指针找出它的虚指针,在找到它的虚表,第n个,把他当成函数指针来调用,由于是通过P来调用,所以P就是this point,所以括号里面的P就是this point。


  其中的((*main)())形式为函数指针。


  同时欢迎大家对于我表述不对的地方加以指正,大家共同进步!谢谢!

这篇关于c++对象模型之vptr(虚指针)和vtbl(虚表)浅析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/667768

相关文章

浅析Spring Security认证过程

类图 为了方便理解Spring Security认证流程,特意画了如下的类图,包含相关的核心认证类 概述 核心验证器 AuthenticationManager 该对象提供了认证方法的入口,接收一个Authentiaton对象作为参数; public interface AuthenticationManager {Authentication authenticate(Authenti

大模型研发全揭秘:客服工单数据标注的完整攻略

在人工智能(AI)领域,数据标注是模型训练过程中至关重要的一步。无论你是新手还是有经验的从业者,掌握数据标注的技术细节和常见问题的解决方案都能为你的AI项目增添不少价值。在电信运营商的客服系统中,工单数据是客户问题和解决方案的重要记录。通过对这些工单数据进行有效标注,不仅能够帮助提升客服自动化系统的智能化水平,还能优化客户服务流程,提高客户满意度。本文将详细介绍如何在电信运营商客服工单的背景下进行

【C++ Primer Plus习题】13.4

大家好,这里是国中之林! ❥前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家。点击跳转到网站。有兴趣的可以点点进去看看← 问题: 解答: main.cpp #include <iostream>#include "port.h"int main() {Port p1;Port p2("Abc", "Bcc", 30);std::cout <<

C++包装器

包装器 在 C++ 中,“包装器”通常指的是一种设计模式或编程技巧,用于封装其他代码或对象,使其更易于使用、管理或扩展。包装器的概念在编程中非常普遍,可以用于函数、类、库等多个方面。下面是几个常见的 “包装器” 类型: 1. 函数包装器 函数包装器用于封装一个或多个函数,使其接口更统一或更便于调用。例如,std::function 是一个通用的函数包装器,它可以存储任意可调用对象(函数、函数

Andrej Karpathy最新采访:认知核心模型10亿参数就够了,AI会打破教育不公的僵局

夕小瑶科技说 原创  作者 | 海野 AI圈子的红人,AI大神Andrej Karpathy,曾是OpenAI联合创始人之一,特斯拉AI总监。上一次的动态是官宣创办一家名为 Eureka Labs 的人工智能+教育公司 ,宣布将长期致力于AI原生教育。 近日,Andrej Karpathy接受了No Priors(投资博客)的采访,与硅谷知名投资人 Sara Guo 和 Elad G

C++11第三弹:lambda表达式 | 新的类功能 | 模板的可变参数

🌈个人主页: 南桥几晴秋 🌈C++专栏: 南桥谈C++ 🌈C语言专栏: C语言学习系列 🌈Linux学习专栏: 南桥谈Linux 🌈数据结构学习专栏: 数据结构杂谈 🌈数据库学习专栏: 南桥谈MySQL 🌈Qt学习专栏: 南桥谈Qt 🌈菜鸡代码练习: 练习随想记录 🌈git学习: 南桥谈Git 🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈🌈�

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

06 C++Lambda表达式

lambda表达式的定义 没有显式模版形参的lambda表达式 [捕获] 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 有显式模版形参的lambda表达式 [捕获] <模版形参> 模版约束 前属性 (形参列表) 说明符 异常 后属性 尾随类型 约束 {函数体} 含义 捕获:包含零个或者多个捕获符的逗号分隔列表 模板形参:用于泛型lambda提供个模板形参的名

Retrieval-based-Voice-Conversion-WebUI模型构建指南

一、模型介绍 Retrieval-based-Voice-Conversion-WebUI(简称 RVC)模型是一个基于 VITS(Variational Inference with adversarial learning for end-to-end Text-to-Speech)的简单易用的语音转换框架。 具有以下特点 简单易用:RVC 模型通过简单易用的网页界面,使得用户无需深入了

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验