C++虚函数表(虚表Virtual Table,简称vtable、VFT)(编译器为支持运行时多态(动态绑定)而自动生成的一种内部数据结构)虚函数指针vptr

文章目录

      • **1. 虚函数表的核心概念**
        • - **虚函数表(vtable)**:
        • - **虚函数指针(vptr)**:
      • **2. 虚函数表的生成与工作流程**
        • **生成时机**
          • - **当一个类中至少有一个虚函数时**,编译器会为该类生成一个虚函数表。
          • - **派生类继承虚函数表**:
        • **调用流程**
          • 1. **对象创建时**:
          • 2. **调用虚函数时**:
      • **3. 示例代码解析**
        • - 以下代码展示了虚函数表的工作原理
        • - **虚函数表的生成**:
        • - **调用过程**:
      • **4. 虚函数表的内存布局**
        • - 以一个简单的类为例
        • - **对象内存布局**:
        • - **虚函数表结构**:
      • **5. 虚函数表的注意事项**
        • 1. **性能开销**:
        • 2. **纯虚函数与抽象类**:
        • 3. **多继承与虚函数表**:
        • 4. **编译器相关**:
      • **6. 虚函数调用流程图**
      • **7. 总结**

C++中的 虚函数表(Virtual Table,简称 vtable)是编译器为支持 运行时多态(动态绑定)而自动生成的一种内部数据结构。它的核心作用是通过 虚函数指针(vptr)和虚函数表的配合,实现基类指针或引用调用派生类重写函数的能力。


1. 虚函数表的核心概念

- 虚函数表(vtable)

每个包含虚函数的类都会有一个虚函数表。这个表是一个指针数组,存储了该类所有虚函数的地址。例如:

  • 如果一个类有3个虚函数,虚函数表就包含3个指针,分别指向这3个函数的实现。
  • 如果派生类重写了某个虚函数,派生类虚函数表中对应位置的指针会被替换为派生类的函数地址。
- 虚函数指针(vptr)

每个包含虚函数的对象在内存中会隐式地包含一个指针(vptr),指向该对象所属类的虚函数表。

  • vptr通常位于对象内存布局的最前面(具体位置可能因编译器而异)。
  • 当通过基类指针或引用调用虚函数时,程序会通过vptr查找虚函数表,找到正确的函数地址并执行。

2. 虚函数表的生成与工作流程

生成时机
- 当一个类中至少有一个虚函数时,编译器会为该类生成一个虚函数表。
- 派生类继承虚函数表
  • 如果派生类没有重写基类的虚函数,则虚函数表中直接继承基类的虚函数地址。
  • 如果派生类重写了某个虚函数,则虚函数表中对应位置的指针会被更新为派生类的函数地址。
调用流程
1. 对象创建时

编译器会自动初始化对象的vptr,使其指向该类的虚函数表。

2. 调用虚函数时
  • 程序通过对象的vptr找到虚函数表。
  • 根据虚函数表中的索引(与函数声明顺序一致)找到对应的函数地址。
  • 调用该地址指向的函数(可能是基类或派生类的实现)。

3. 示例代码解析

- 以下代码展示了虚函数表的工作原理
#include <iostream>
using namespace std;class Base {
public:virtual void func() { cout << "Base::func()" << endl; }
};class Derived : public Base {
public:void func() override { cout << "Derived::func()" << endl; }
};int main() {Base* ptr = new Derived();ptr->func();  // 输出 "Derived::func()"delete ptr;return 0;
}
- 虚函数表的生成
  • Base类有一个虚函数func(),因此编译器为Base生成一个虚函数表,表中存储Base::func()的地址。
  • Derived类重写了func(),因此编译器为Derived生成一个新的虚函数表,表中存储Derived::func()的地址。
- 调用过程
  • ptrBase*类型,但指向Derived对象。
  • 调用ptr->func()时,程序通过Derived对象的vptr找到Derived的虚函数表,调用Derived::func()

4. 虚函数表的内存布局

- 以一个简单的类为例
class Base {
public:virtual void func1() {}virtual void func2() {}
};
- 对象内存布局

每个Base对象的内存布局如下:

[vptr] -> 指向Base的虚函数表
[其他成员变量]
Base
+virtual void func1()
+virtual void func2()
«data»
BaseObject
vptr --> Base
data1: int
data2: double
- 虚函数表结构
Base的虚函数表:
+-----------------+
| func1() 的地址  |
+-----------------+
| func2() 的地址  |
+-----------------+
Base的虚函数表
func1() 地址
func2() 地址

5. 虚函数表的注意事项

1. 性能开销
  • 调用虚函数需要通过两次间接寻址(vptr → 虚函数表 → 函数地址),比直接调用普通函数稍慢。
  • 每个对象需要额外存储一个vptr,增加了内存占用。
2. 纯虚函数与抽象类
  • 如果类中包含纯虚函数(virtual void func() = 0;),则该类为抽象类,不能实例化对象。
  • 纯虚函数在虚函数表中通常用特殊标记(如NULL)表示。
3. 多继承与虚函数表
  • 多继承情况下,对象可能包含多个vptr,分别指向不同基类的虚函数表。
  • 虚函数表的管理会更复杂,但核心原理与单继承相同。
Derived对象
vptr1 (Base1)
vptr2 (Base2)
Base1::func()
Base2::func()
4. 编译器相关
  • 虚函数表是编译器的实现细节,C++标准未规定具体实现方式。主流编译器(如GCC、MSVC)均采用类似机制。

6. 虚函数调用流程图

以下是虚函数调用的完整流程:

Base* ptr = new Derived();
ptr->func()
vptr 指向 Derived 的虚函数表?
查找虚函数表中 func() 的地址
调用 Derived::func()
查找虚函数表中 func() 的地址
调用 Base::func()

7. 总结

虚函数表是C++实现运行时多态的核心机制。通过虚函数表和虚函数指针的配合,C++能够在运行时根据对象的实际类型动态选择正确的函数实现。这种机制虽然带来了一定的性能和内存开销,但极大地增强了代码的灵活性和可扩展性,是面向对象编程中多态特性的基石。

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

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

相关文章

使用Python和TensorFlow实现图像分类

最近研学过程中发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击链接跳转到网站人工智能及编程语言学习教程。读者们可以通过里面的文章详细了解一下人工智能及其编程等教程和学习方法。下面开始对正文内容的…

Unity UI 性能优化--Sprite 篇

&#x1f3af; Unity UI 性能优化终极指南 — Sprite篇 &#x1f9e9; Sprite 是什么&#xff1f;—— 渲染的基石与性能的源头 在Unity的2D渲染管线中&#xff0c;Sprite 扮演着至关重要的角色。它不仅仅是2D图像资源本身&#xff0c;更是GPU进行渲染批处理&#xff08;Batch…

【git】把本地更改提交远程新分支feature_g

创建并切换新分支 git checkout -b feature_g 添加并提交更改 git add . git commit -m “实现图片上传功能” 推送到远程 git push -u origin feature_g

vue中加载Cesium地图(天地图、高德地图)

目录 1、将下载的Cesium包移动至public下 2、首先需要将Cesium.js和widgets.css文件引入到 3、 新建Cesium.js文件&#xff0c;方便在全局使用 4、新建cesium.vue文件&#xff0c;展示三维地图 1、将下载的Cesium包移动至public下 npm install cesium后​​​​​​​ 2、…

Elasticsearch的插件(Plugin)系统介绍

Elasticsearch的插件(Plugin)系统是一种扩展机制,允许用户通过添加自定义功能来增强默认功能,而无需修改核心代码。插件可以提供从分析器、存储后端到安全认证、机器学习等各种功能,使Elasticsearch能够灵活适应不同的应用场景和业务需求。 一、插件的核心特点 模块化扩展…

基于 openEuler 22.03 LTS SP1 构建 DPDK 22.11.8 开发环境指南

基于 openEuler 22.03 LTS SP1 构建 DPDK 22.11.8 开发环境指南 本文详细介绍了在 openEuler 22.03 LTS SP1 操作系统上构建 DPDK 22.11.8 开发环境的完整流程。DPDK 20 版本之后采用 mesonninja 的编译方式&#xff0c;与早期版本有所不同。本文内容也可作为其他 Linux 发行版…

微服务网关SpringCloudGateway+SaToken鉴权

目录 概念 前置知识回顾 拿到UserInfo 用于自定义权限和角色的获取逻辑 最后进行要进行 satoken 过滤器全局配置 概念 做权限认证的时候 我们首先要明确两点 我们需要的角色有几种 我们需要的权限有几种 角色 分两种 ADMIN 管理员 &#xff1a;可管理商品 CUSTIOMER 普通…

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决

Spring Cloud Gateway 中自定义验证码接口返回 404 的排查与解决 问题背景 在一个基于 Spring Cloud Gateway WebFlux 构建的微服务项目中&#xff0c;新增了一个本地验证码接口 /code&#xff0c;使用函数式路由&#xff08;RouterFunction&#xff09;和 Hutool 的 Circle…

Dify中聊天助手、agent、文本生成、chatflow、工作流模式解读分析与对比

一次解读 1. 聊天助手 (Chat Assistant) 情景定位 (Situation): 你需要创建一个可以与用户进行多轮对话的AI应用&#xff0c;例如客服机器人、信息查询助手、或一个特定领域的虚拟专家。目标明确 (Purpose): 核心目标是理解并响应用户的连续提问&#xff0c;维持对话的上下文…

使用Node.js分片上传大文件到阿里云OSS

阿里云OSS的分片上传&#xff08;Multipart Upload&#xff09;是一种针对大文件优化的上传方式&#xff0c;其核心流程和关键特性如下&#xff1a; 1. ‌核心流程‌ 分片上传分为三个步骤&#xff1a; 初始化任务‌&#xff1a;调用InitiateMultipartUpload接口创建上传任务…

C++ if语句完全指南:从基础到工程实践

一、选择结构在程序设计中的核心地位 程序流程控制如同城市交通网络&#xff0c;if语句则是这个网络中的决策枢纽。根据ISO C标准&#xff0c;选择结构占典型项目代码量的32%-47%&#xff0c;其正确使用直接影响程序的&#xff1a; 逻辑正确性 执行效率 可维护性 安全边界 …

【大模型LLM学习】Flash-Attention的学习记录

【大模型LLM学习】Flash-Attention的学习记录 0. 前言1. flash-attention原理简述2. 从softmax到online softmax2.1 safe-softmax2.2 3-pass safe softmax2.3 Online softmax2.4 Flash-attention2.5 Flash-attention tiling 0. 前言 Flash Attention可以节约模型训练和推理时间…

python打卡day46@浙大疏锦行

知识点回顾&#xff1a; 不同CNN层的特征图&#xff1a;不同通道的特征图什么是注意力&#xff1a;注意力家族&#xff0c;类似于动物园&#xff0c;都是不同的模块&#xff0c;好不好试了才知道。通道注意力&#xff1a;模型的定义和插入的位置通道注意力后的特征图和热力图 内…

JavaSec-SPEL - 表达式注入

简介 SPEL(Spring Expression Language)&#xff1a;SPEL是Spring表达式语言&#xff0c;允许在运行时动态查询和操作对象属性、调用方法等&#xff0c;类似于Struts2中的OGNL表达式。当参数未经过滤时&#xff0c;攻击者可以注入恶意的SPEL表达式&#xff0c;从而执行任意代码…

SpringCloud——OpenFeign

概述&#xff1a; OpenFeign是基于Spring的声明式调用的HTTP客户端&#xff0c;大大简化了编写Web服务客户端的过程&#xff0c;用于快速构建http请求调用其他服务模块。同时也是spring cloud默认选择的服务通信工具。 使用方法&#xff1a; RestTemplate手动构建: // 带查询…

【深入学习Linux】System V共享内存

目录 前言 一、共享内存是什么&#xff1f; 共享内存实现原理 共享内存细节理解 二、接口认识 1.shmget函数——申请共享内存 2.ftok函数——生成key值 再次理解ftok和shmget 1&#xff09;key与shmid的区别与联系 2&#xff09;再理解key 3&#xff09;通过指令查看/释放系统中…

探索 Java 垃圾收集:对象存活判定、回收流程与内存策略

个人主页-爱因斯晨 文章专栏-JAVA学习笔记 热门文章-赛博算命 一、引言 在 Java 技术体系里&#xff0c;垃圾收集器&#xff08;Garbage Collection&#xff0c;GC&#xff09;与内存分配策略是自动内存管理的核心支撑。深入探究其原理与机制&#xff0c;对优化程序内存性能…

hbase资源和数据权限控制

hbase适合大数据量下点查 https://zhuanlan.zhihu.com/p/471133280 HBase支持对User、NameSpace和Table进行请求数和流量配额限制&#xff0c;限制频率可以按sec、min、hour、day 对于请求大小限制示例&#xff08;5K/sec,10M/min等&#xff09;&#xff0c;请求大小限制单位如…

大数据-275 Spark MLib - 基础介绍 机器学习算法 集成学习 随机森林 Bagging Boosting

点一下关注吧&#xff01;&#xff01;&#xff01;非常感谢&#xff01;&#xff01;持续更新&#xff01;&#xff01;&#xff01; 大模型篇章已经开始&#xff01; 目前已经更新到了第 22 篇&#xff1a;大语言模型 22 - MCP 自动操作 FigmaCursor 自动设计原型 Java篇开…

Delphi 实现远程连接 Access 数据库的指南

方法一&#xff1a;通过局域网共享 Access 文件&#xff08;简单但有限&#xff09; 步骤 1&#xff1a;共享 Access 数据库 将 .mdb 或 .accdb 文件放在局域网内某台电脑的共享文件夹中。 右键文件夹 → 属性 → 共享 → 启用共享并设置权限&#xff08;需允许网络用户读写&a…