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

C++迈向精通:当我尝试修改虚函数表

尝试修改虚函数表

本期纯整活儿好吧!!!!

初衷

有一天我突然开始好奇虚函数表是否真的存在,于是我开始想是否能够从C++中查看或者调用虚函数表中的内容。,于是有了下面的操作。

操作过程

起初我并没有思路,但是我知道,每一个类对应一个虚函数表,因此首先我需要一个虚函数,因此我随便写了一个基类:

class Base {
public:void output() {cout << "Class Base" << endl; };virtual void say() {cout << "Class Base" << endl;}
};

然后写一个子类,去 override 一下他的这个函数:

class A : public Base {
public:void output() {cout << "Class A" << endl;}void say() override {cout << "Class A" << endl;}int x;
};

然后按照同样的方式再创建一个 B 类:

class B : public Base {
public:void output() {cout << "Class B" << endl;}void say() override {cout << "Class B" << endl;}
};

这样以来,应该会有三个虚函数表,分别是:

  • Base基类对应的虚函数表
  • A类对应的虚函数表
  • B类对应的虚函数表

然后如何调用他们呢?我想了好久,想出这样的一个方法:

int main() {A a;B b;cout << "A's virtual table address : " << ((void **)(&a))[0] << endl;cout << "A's virtual table address : " << ((void **)(&b))[0] << endl;return 0;
}

根据理论来说,C++中的虚函数表应该在类内空间的第一个位置,占八个字节,是一个指向函数表的指针,那么我们就应该这样做:

((void **)(&b))[0];

这会返回一个虚函数表的地址。

这句话是什么意思呢?首先我们要清楚,对象的空间分配与结构体是一样的,而根据理论来看,虚函数表的指针会被编译器自动添加在对象空间的初始位置,也就是说,对象所在的空间的第一个单元存储的是虚函数表的地址。

如何获得这个首地址呢?首先我们要像取数组首地址一样,用取地址符号获得对象的首地址。然后将其强制转换为 (void **) 类型,这相当于让电脑将这个对象的空间看作一个数组,这个数组中存放的全部都是指向 void * 类型的数据的地址。

void * 类型是函数指针类型,我们不用管,最后在末尾添加[0]就相当于得到了虚函数表的地址。

尝试输出一下:
1
嗯,看起来没啥问题,但是如何证明他是个虚函数表的地址呢?

我能否将一个类中的修改到另一个虚函数表中?然后让这个对象执行的时候出现另外一个类的动作?

于是我开始了下面的尝试:

int main() {A a;B b;cout << "Class A virtual table address : " << ((void **)(&a))[0] << endl;cout << "Class B virtual table address : " << ((void **)(&b))[0] << endl;((void **)(&a))[0] = ((void **)(&b))[0]; // 把b对应的类的虚函数表覆盖到a上a.say(); // 如果虚函数表被覆盖了的话,那么就会出现a执行了b的say方法的状况b.say();return 0;
}

然而结果是这样的:

2
发现结果并没有被改变,这是怎么回事?我百思不得其解,多方询问过之后了解到是gcc编译器把我的虚函数的调用过程给优化掉了,无奈我只能使用指针和引用来赋值:

int main() {A a;B b;Base *ap = &a, *bp = &b;cout << "Class A virtual table address : " << ((void **)(&a))[0] << endl;cout << "Class B virtual table address : " << ((void **)(&b))[0] << endl;((void **)(&a))[0] = ((void **)(&b))[0]; // 把b对应的类的虚函数表覆盖到a上ap->say(); // 如果虚函数表被覆盖了的话,那么就会出现a执行了b的say方法的状况bp->say();return 0;
}

3
执行成功啦!!!

其实后面我还做了很多好玩的操作,这里先不放出来,写的有点累,下次再凑出一篇来!

:wq 拜拜~~

相关文章:

C++迈向精通:当我尝试修改虚函数表

尝试修改虚函数表 本期纯整活儿好吧&#xff01;&#xff01;&#xff01;&#xff01; 初衷 有一天我突然开始好奇虚函数表是否真的存在&#xff0c;于是我开始想是否能够从C中查看或者调用虚函数表中的内容。&#xff0c;于是有了下面的操作。 操作过程 起初我并没有思路…...

IDEA 高效插件工具

文章目录 LombokMaven Helper 依赖冲突any-rule(正则表达式插件)快速生成javadocGsonFormat (Aits) 将json解析成类Diagrams使用 类图SequenceDiagram时序图GenerateAllSetter&#xff08;AltEnter&#xff09;大小写转写String ManipulationGitToolBox 代码提交人activate-pow…...

SQL入门大全

SQL&#xff08;Structured Query Language&#xff0c;结构化查询语言&#xff09;是一种用于管理关系型数据库的标准编程语言。它具有数据操纵和数据定义等多种功能&#xff0c;为数据库管理系统提供了强大的交互性特点&#xff0c;能够极大地提高计算机应用系统的工作质量与…...

【深度优先搜索 广度优先搜索】297. 二叉树的序列化与反序列化

本文涉及知识点 深度优先搜索 广度优先搜索 深度优先搜索汇总 图论知识汇总 LeetCode297. 二叉树的序列化与反序列化 序列化是将一个数据结构或者对象转换为连续的比特位的操作&#xff0c;进而可以将转换后的数据存储在一个文件或者内存中&#xff0c;同时也可以通过网络传…...

App UI 风格,引领设计风向

App UI 风格&#xff0c;引领设计风向...

TIM—通用定时器高级定时器

通用/高级定时器的功能 在基本定时器功能的基础上新增功能&#xff1a; 通用定时器有4个独立通道&#xff0c;且每个通道都可以用于下面功能。 &#xff08;1&#xff09;输入捕获&#xff1a;测量输入信号的周期和占空比等。 &#xff08;2&#xff09;输出比较&#xff1a;产…...

【数据结构与算法(C语言)】循环队列图解

目录 1. 前言1.1 普通循环队列假溢出1.1.1 初始化队列1.1.2 插满队列1.1.3 删除元素后&#xff0c;再插入元素 1.2 循环队列1.2.1 插入元素&#xff0c;队列已满1.2.2 将元素J1、J2出列&#xff0c;循环队列又空出两个空间1.2.3 元素J6可以继续入列 2. 存储结构和函数说明2.1 队…...

私域流量转化不济的原因

你是不是也曾感到私域流量的转化一直不如意&#xff1f;让我来告诉你&#xff0c;这六大问题是为什么&#xff0c;以及如何轻松解决它们&#xff0c;提升你的私域流量转化率&#xff01; 1. 问题&#xff1a;目标不明确 你是否常常感到茫然&#xff0c;不知道私域流量应该有何目…...

百万上下文RAG,Agent还能这么玩

❝ 在AI技术飞速发展的今天&#xff0c;我们见证了许多令人惊叹的突破。最近&#xff0c;Qwen2模型的开源引起了广泛的关注&#xff0c;它不仅展示了超越闭源模型的能力&#xff0c;还带来了一个全新的框架——Qwen-Agent。 Qwen-Agent的设计思路虽然与LangChain相似&#xff0…...

【后端开发】服务开发场景之高可用(冗余设计,服务限流,降级熔断,超时重试,性能测试)

【后端开发】服务开发场景之高可用&#xff08;冗余设计&#xff0c;服务限流&#xff0c;降级熔断&#xff0c;超时重试&#xff0c;性能测试&#xff09; 文章目录 序&#xff1a;如何设计一个高可用的系统&#xff1f;可用性的判断指标是什么&#xff1f;哪些情况会导致系统…...

在 Selenium 中更改 User-Agent | 步骤与最佳实践

在 Selenium 中更改 User Agent 是许多网页抓取任务中的关键步骤。它有助于将自动化脚本伪装成常规浏览器&#xff0c;从而避免被网站检测到。本指南将带您了解如何在 Selenium 中更改 Google Chrome 的 User Agent&#xff0c;并提供最佳实践以确保您的网页抓取任务顺利进行。…...

2024酒店IPTV云桌面系统建设方案

Hello大家好&#xff0c;我是点量小芹&#xff0c;这一年多的时间一直在分享实时云渲染像素流相关的内容&#xff0c;今天和大家聊聊酒店IPTV云桌面电视系统解决方案&#xff0c;或者有的朋友也会称之为IPTV服务器。熟悉小芹的朋友知道&#xff0c;IPTV软件系统是我们一直在推的…...

java Thrift TThreadPoolServer 多个processor 的实现

当我们使用Thrift 通信的时候&#xff0c;服务端有时候需要注册多个类&#xff0c;去实现通信&#xff0c;这时候我们就不能再使用单一Processor的方式&#xff0c;就要使用多个Processor&#xff0c;那么如何去实现呢&#xff1f; 多个Process 服务端 public static void m…...

失眠焦虑的解脱之道:找回内心的平静

&#x1f343; 在这个快节奏的时代&#xff0c;失眠与焦虑似乎成了许多人的隐形敌人。每当夜幕降临&#xff0c;它们便悄悄潜入心底&#xff0c;扰乱我们的思绪&#xff0c;让宁静的夜晚变得无比漫长。然而&#xff0c;生活总有办法让我们找回内心的平静&#xff0c;只需稍作调…...

OLED柔性屏的显示效果如何

OLED柔性屏的显示效果非常出色&#xff0c;具有多方面的优势。以下是关于OLED柔性屏显示效果的详细分析&#xff1a; 色彩表现&#xff1a;OLED柔性屏的每个像素都可以独立发光&#xff0c;因此色彩准确性极高。黑色呈现得非常深邃&#xff0c;而亮部则展现出鲜明而生动的细节。…...

百货商城优选 伊利牛奶推出全国首款减甲烷环保学生奶

近日&#xff0c;伊利集团受邀参加在全国首个“国际首脑峰会零碳场馆”召开的“降碳增产科技助力奶业绿色高质量发展”首款低碳饲料创新大会。会上&#xff0c;伊利宣布将推出全国首款减甲烷环保学生牛奶——伊利QQ星学生纯牛奶&#xff0c;进一步将可持续发展落到实处&#xf…...

Fluid 1.0 版发布,打通云原生高效数据使用的“最后一公里”

作者&#xff1a;顾荣 前言 得益于云原生技术在资源成本集约、部署运维便捷、算力弹性灵活方面的优势&#xff0c;越来越多企业和开发者将数据密集型应用&#xff0c;特别是 AI 和大数据领域应用&#xff0c;运行于云原生环境中。然而&#xff0c;云原生计算与存储分离架构虽…...

软件测试--第十一章 设计和维护测试用例

1.单选题 (2分) 下面有关测试设计的叙述,说法不正确的是( )。 A 测试用例的设计是一项技术性强.智力密集型的活动 B 在开展测试用例设计前,必须将测试需求进行详细展开 C 在一般的测试组织内,测试用例的评审可能不是正式的评审会 D 在测试用例设计时,只设计覆盖正常流程和操…...

前端只允许一次函数调用

如果你正在进行前端开发&#xff0c;并且只想允许一次函数调用&#xff0c;你可以使用JavaScript的闭包结构创建一个只能被调用一次的函数。这样的函数有时被称为单次调用函数&#xff08;“one-time call” functions&#xff09;或一次性函数&#xff08;“once” functions&…...

visdom使用时所遇的问题及解决方法

最近在用visdom进行可视化的过程中&#xff0c;虽然可有效的避免主机拒绝访问&#xff08;该问题的解决方法&#xff0c;请参考深度学习可视化工具visdom使用-CSDN博客&#xff09;即在终端输入python -m visom.server 1.训练过程中visdom出现ValueError: too many file descr…...

使用分级同态加密防御梯度泄漏

抽象 联邦学习 &#xff08;FL&#xff09; 支持跨分布式客户端进行协作模型训练&#xff0c;而无需共享原始数据&#xff0c;这使其成为在互联和自动驾驶汽车 &#xff08;CAV&#xff09; 等领域保护隐私的机器学习的一种很有前途的方法。然而&#xff0c;最近的研究表明&…...

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别

OpenPrompt 和直接对提示词的嵌入向量进行训练有什么区别 直接训练提示词嵌入向量的核心区别 您提到的代码: prompt_embedding = initial_embedding.clone().requires_grad_(True) optimizer = torch.optim.Adam([prompt_embedding...

用docker来安装部署freeswitch记录

今天刚才测试一个callcenter的项目&#xff0c;所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...

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

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

力扣-35.搜索插入位置

题目描述 给定一个排序数组和一个目标值&#xff0c;在数组中找到目标值&#xff0c;并返回其索引。如果目标值不存在于数组中&#xff0c;返回它将会被按顺序插入的位置。 请必须使用时间复杂度为 O(log n) 的算法。 class Solution {public int searchInsert(int[] nums, …...

JVM 内存结构 详解

内存结构 运行时数据区&#xff1a; Java虚拟机在运行Java程序过程中管理的内存区域。 程序计数器&#xff1a; ​ 线程私有&#xff0c;程序控制流的指示器&#xff0c;分支、循环、跳转、异常处理、线程恢复等基础功能都依赖这个计数器完成。 ​ 每个线程都有一个程序计数…...

纯 Java 项目(非 SpringBoot)集成 Mybatis-Plus 和 Mybatis-Plus-Join

纯 Java 项目&#xff08;非 SpringBoot&#xff09;集成 Mybatis-Plus 和 Mybatis-Plus-Join 1、依赖1.1、依赖版本1.2、pom.xml 2、代码2.1、SqlSession 构造器2.2、MybatisPlus代码生成器2.3、获取 config.yml 配置2.3.1、config.yml2.3.2、项目配置类 2.4、ftl 模板2.4.1、…...

MySQL:分区的基本使用

目录 一、什么是分区二、有什么作用三、分类四、创建分区五、删除分区 一、什么是分区 MySQL 分区&#xff08;Partitioning&#xff09;是一种将单张表的数据逻辑上拆分成多个物理部分的技术。这些物理部分&#xff08;分区&#xff09;可以独立存储、管理和优化&#xff0c;…...

Kubernetes 网络模型深度解析:Pod IP 与 Service 的负载均衡机制,Service到底是什么?

Pod IP 的本质与特性 Pod IP 的定位 纯端点地址&#xff1a;Pod IP 是分配给 Pod 网络命名空间的真实 IP 地址&#xff08;如 10.244.1.2&#xff09;无特殊名称&#xff1a;在 Kubernetes 中&#xff0c;它通常被称为 “Pod IP” 或 “容器 IP”生命周期&#xff1a;与 Pod …...

MySQL 主从同步异常处理

阅读原文&#xff1a;https://www.xiaozaoshu.top/articles/mysql-m-s-update-pk MySQL 做双主&#xff0c;遇到的这个错误&#xff1a; Could not execute Update_rows event on table ... Error_code: 1032是 MySQL 主从复制时的经典错误之一&#xff0c;通常表示&#xff…...