75 C++对象模型探索。C++关于 虚函数表指针位置分析

如果一个类中,有虚函数,针对这个类会产生一个虚函数表

生成这个类对象的时候,会有一个虚函数表指针,这个指针会指向这个虚函数表的开始地址。

我们本节就研究这个vptr指针。注意,vptr指针在 类对象中的位置。

证明 虚函数表指针 存在类对象中

class Teacher37 {
public:

	virtual void Teacher37func1() {
		cout << "Teacher37func1 start " << endl;
	}
	virtual void Teacher37func2() {
		cout << "Teacher37func2 start " << endl;
	}
	virtual int Teacher37func3() {
		cout << "Teacher37func3 start " << endl;
		return 888;
	}
public:
	int mage;
};

void main() {
	cout << sizeof(int) << endl;//vs2017 32bit下大小为 4   64位下大小为8
	cout << sizeof(int *) << endl;//vs 2017 32bit下大小为 4   64位下大小为4
	cout<<sizeof(Teacher37)<<endl;//vs 2917 32bit下大小为 8   64位下大小为16,因为有内存对齐,因此是16

	//我们当前以32bit下研究,sizeof(Teacher37)大小为8,说明除了int mage占用4个字节外,还有4个字节被占用
	//这4个字节就是vptr指针的大小

}

证明 虚函数表指针 的位置,以及如何找到这个指针的位置并调用

class Teacher37 {
public:

	virtual void Teacher37func1() {
		cout << "Teacher37func1 start " << endl;
	}
	virtual void Teacher37func2() {
		cout << "Teacher37func2 start " << endl;
	}
	virtual int Teacher37func3() {
		cout << "Teacher37func3 start " << endl;
		return 888;
	}
public:
	int mage;
};

//证明vptr指针的位置
void main() {
	Teacher37 * ptea = new Teacher37();
	long * templong = reinterpret_cast<long *>(ptea);
	cout << "ptea address = " << ptea << "  templong = " << templong<< endl;
	int *pmage = &(ptea->mage);
	cout << "pmage = " << pmage << endl;
	int *pmagePrevious = pmage - 1;
	cout << "pmagePrevious = " << pmagePrevious << endl;
	delete ptea;

	//  ptea address = 01060880  templong = 01060880
	//	pmage = 01060884
	//	pmagePrevious = 01060880
	//从上述结果可以看到,Teacher37对象的大小是8,mage占用了4个,而且是后4个字节,那么vptr占用的是前4个字节。


	//如何找到这个vptr指向的函数,并且调用.
	//思路是使用vptr指针的++ 或者vptr指针[], 找到vptr指针指向的函数,那么首先就要找到 指向func的指针,
	//又因为 指针的++,步长为指针指向的数据的 长度,因此我们要找到 vptr指向的
	Teacher37 * ptea1 = new Teacher37();
	//将其 强制转换成 long*
	long * tempptea1 = reinterpret_cast<long *>(ptea1);

	
	//注意这时候还要转,先写出来,后面再解释
	//tempptea1 的指向就是当前Teacher37,由于vptr是Teacher37的第一个“元素”,因此*tempptea1指向的是teacher37,也指向的是vptr,从前面的知识我们知道vptr指向的数据就是 虚函数表
	long *vptr= (long *)(*tempptea1);

	cout << sizeof(long) << endl;//在32位下 long也占用4个字节,int要占用4个字节,指针也占用4个字节,因此可以使用long * 接受。

	typedef void(*FUNTYPE)(void);//定义函数指针类型 返回值是void,参数也是void

	//用函数指针类型 定义一个变量,注意这个变量是一个指针,
	//通过 templong1[x] 指向不同的指针,这块要看懂,需要懂函数指针的意义,如果不懂,或者不清晰,可以参考https://mp.csdn.net/mp_blog/creation/editor/135103006
	FUNTYPE funpoint1 = (FUNTYPE)(vptr[0]);
	FUNTYPE funpoint2 = (FUNTYPE)(vptr[1]);
	FUNTYPE funpoint3 = (FUNTYPE)(vptr[2]);

	//那么这时候 理论 funpoint1 就指向Teacher37func1()方法了
	//调用:
	cout << "---------------" << endl;
	funpoint1();
	funpoint2();
	funpoint3();
	cout << "---------------" << endl;

	delete ptea1;

	//ptea address = 00A308C8  templong = 00A308C8
	//	pmage = 00A308CC
	//	pmagePrevious = 00A308C8
	//	4
	//	-------------- -
	//	Teacher37func1 start
	//	Teacher37func2 start
	//	Teacher37func3 start
	//	-------------- -
}

上述代码关键点解析:从基础开始捋一遍,理解不了的地方,画内存模型图,就能看明白了

class Teacher38 {
public:
	int *p;
	long long templong;
};

void main() {
	//相关知识点整理:
	//1. 指针的[],
	int *pint = new int[3];//定义一个指针,指向一个3个int的空间
	for (size_t i = 0; i < 3; i++)
	{
		pint[i] = i * 6;
	}
	for (size_t i = 0; i < 3; i++)
	{
		cout << pint[i] << endl;
	}
	cout << "----------------------" << endl;
	cout<<pint<<endl;
	for (size_t i = 0; i < 3; i++)
	{
		cout << &(pint[i]) << endl;
	}

	//  0
	//	6
	//	12
	//	----------------------
	//	0121EA98
	//	0121EA98  指针的[0]  和指针的[1]之间的差距是 指针指向的数据,由于指针是int *pint,指针指向的数据是int,因此[0]和[1]相差一个int的大小,在32位 vs2017上是4个字节
	//	0121EA9C
	//	0121EAA0

	//2 指针的++,即指针的步长
	cout <<"sizeof(long long) = " <<  sizeof(long long) << endl;//在 win32 上 是8
	long long * plong = new long long[5];
	long long * tempplong = plong;
	for (size_t i = 0; i < 5; i++)
	{
		//打印 tempplong 指向的空间的地址是啥
		cout << tempplong << endl;
		*tempplong = i * 8;//给tempplong指向的空间的内容赋值
		tempplong++;// ++后,再次循环查看plong 的地址
	}

	//从结果来看,tempplong++的每次加的是8,也就是long long 的大小
	//sizeof(long long) = 8
	//	00D26E00
	//	00D26E08
	//	00D26E10
	//	00D26E18
	//	00D26E20

	for (int i = 0; i < 5;i++) {
		cout << *plong << endl;
		plong++;
	}

	//  0
	//	8
	//	16
	//	24
	//	32
	

	//3.类中 变量 使用的 技巧,我们这里以Teacher38 为例分析
	Teacher38 tea;
	tea.p = new int[3];
	for (size_t i = 0; i < 3; i++)
	{
		//后面还要使用tea.p,因此不要使用++操作,
		tea.p[i] = i * 8;
	}
	tea.templong = 999;

	cout << "正常使用 start " << endl;
	for (size_t i = 0; i < 3; i++)
	{
		cout << "tea.p["<< i << "]" << tea.p[i] << endl;
	}
	cout << "tea.templong = " << tea.templong << endl;
	cout << "正常使用 end " << endl;

	cout << "这里要明白指针本身,和指针指向内容的两个概念,复习一下" << endl;
	cout << "打印&tea的地址和&tea.p的地址 这两个是一样的 &tea = " << &tea << "  &tea.p = " << &tea.p << endl;
	cout << "&tea.p 和 tea.p 这两个是不一样的 &tea.p代表的是指针的地址,tea.p代表的是指针指向的内容,虽然这个内容也是地址,&tea.p = " << &tea.p << "  tea.p = " << tea.p << endl;
	
	//4. 下面我们就要模仿上述代码 ,在不知道vptr地址的情况下,只是知道&tea的地址,怎么访问vptr指向的数据
	Teacher38 *ptea = &tea;
	cout << "ptea 指向是tea的地址 ,因此 ptea 和 &tea是一样的" << ptea << "   & tea = " << &tea << endl;
	
	//那么怎么通过已经知道的ptea (也就是&tea的值)算出来  ptea->teapptea的值呢?
	//4.1 先将 ptea 强制类型转换成 long *,为什么是long* 呢?这是因为 指针类型的大小是 long,也就是常说的32 位下是4,64位下是8,实际上是long
	long * tempptea = (long *)ptea; 
	cout << "tempptea = " << tempptea << endl;
	//那么这时候 tempptea 和ptea是指向同一个地址了,注意这里:但是会被强转成long *

	//4.2 然后 再 使用 *tempptea, 将tempptea中的值取出来,我们知道在C语言中 *在取值的时候,代表将teaptea指向的数据取出来
	//那么*tempptea 就代表 temmptea 指向内容的,temptea指向的内容是tea tea的内部布局是这样的,第一个变量是int *p,因此可以用 long *p 接,


	long* temmptea2 = (long *)(*tempptea);
	cout << "temmptea2 = " << temmptea2 << endl;//实际上就是 int *p中存储的值

	for (size_t i = 0; i < 3; i++)
	{
		cout << "temmptea2 = " << &(temmptea2[i]) << endl;
		cout << "temmptea2[" << i << "] = " << temmptea2[i] << endl;
	}

	//正常使用 start
	//	tea.p[0]0
	//	tea.p[1]8
	//	tea.p[2]16
	//	tea.templong = 999
	//	正常使用 end
	//	这里要明白指针本身,和指针指向内容的两个概念,复习一下
	//	打印&tea的地址和&tea.p的地址 这两个是一样的 &tea = 00C1FA0C  &tea.p = 00C1FA0C
	//	&tea.p 和 tea.p 这两个是不一样的 &tea.p代表的是指针的地址,tea.p代表的是指针指向的内容,虽然这个内容也是地址,&tea.p = 00C1FA0C  tea.p = 010A0D98
	//	ptea 指向是tea的地址, 因此 ptea 和 &tea是一样的00C1FA0C   & tea = 00C1FA0C
	//	tempptea = 00C1FA0C
	//	temmptea2 = 010A0D98
	//	temmptea2 = 010A0D98
	//	temmptea2[0] = 0
	//	temmptea2 = 010A0D9C
	//	temmptea2[1] = 8
	//	temmptea2 = 010A0DA0
	//	temmptea2[2] = 16
}

基类和子类中有虚函数表

假设第二个虚函数,子类有重写 基类的第二个虚函数,图标如下

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【算法】糖果(差分约束)

题目 幼儿园里有 N 个小朋友&#xff0c;老师现在想要给这些小朋友们分配糖果&#xff0c;要求每个小朋友都要分到糖果。 但是小朋友们也有嫉妒心&#xff0c;总是会提出一些要求&#xff0c;比如小明不希望小红分到的糖果比他的多&#xff0c;于是在分配糖果的时候&#xff…

echarts 绘制垂直滚动热力图

问题1&#xff1a;提示功能无效 问题2&#xff1a;值筛选无效 效果 在线浏览 下载echarts官网例子(heatmap Examples - Apache ECharts) 稍作改动&#xff1a; generateData 入参改为长度和宽度noise.perlin2(i / 40, j / 20) Math.random() * 5y轴倒置指定zlevel为2 通过定…

链表--226. 翻转二叉树/medium 理解度A

226. 翻转二叉树 1、题目2、题目分析3、复杂度最优解代码示例4、适用场景 1、题目 给你一棵二叉树的根节点 root &#xff0c;翻转这棵二叉树&#xff0c;并返回其根节点。 示例 1&#xff1a; 输入&#xff1a;root [4,2,7,1,3,6,9] 输出&#xff1a;[4,7,2,9,6,3,1]示例 2&…

python:socket基础操作(3)-《udp接收消息》

收跟发基本核心思想差不多&#xff0c;只不过收信息需要去绑定一下端口&#xff0c;如果我们发信息没有绑定端口&#xff0c;那系统会随机分配一个&#xff0c;如果是收信息&#xff0c;那我们必须要求自己绑定端口才行 基础的接收数据 import socketudp_socket socket.socke…

华清远见作业第三十三天——C++(第二天)

思维导图&#xff1a; 题目&#xff1a; 自己封装一个矩形类(Rect)&#xff0c;拥有私有属性:宽度(width)、高度(height)&#xff0c; 定义公有成员函数&#xff1a; 初始化函数&#xff1a;void init(int w, int h) 更改宽度的函数&#xff1a;set_w(int w) 更改高度的函数…

如何使用 WebRTC 与 Kurento 建立视频会议 App

本文作者 WebRTC Ventures 工程师。在 RTC 2018 实时互联网大会上&#xff0c;WebRTC Ventures 的资深软件工程师&#xff0c;将围绕 WebRTC 开发带来经验分享。欢迎访问RTC 开发者社区&#xff0c;与更多WebRTC开发者交流经验。 了解 WebRTC 如何工作的一种简单方式是通过学习…

安全防御综合组网实验

题目 要求 生产区在工作时间可以访问服务器区&#xff0c;仅可以访问http服务器。办公区全天可以访问服务器区&#xff0c;其中10.0.2.20 可以访问FTP服务器和http服务器。10.0.2.10仅可以ping通10.0.3.10。办公区在访问服务器区时采用匿名认证的方式进行上网行为管理。办公区…

20.云原生之GitLab集成Runner

云原生专栏大纲 文章目录 GitLab RunnerGitLab Runner 介绍GitLab Runner分类GitLab Runner工作流程 Gitlab集成Gitlab RunnerGitLab Runner 版本选择Runner在CitLab中位置专用Runner在gitlab中位置群组Runner在gitlab中位置共享Runner在gitlab中位置 GitLab部署Gitlab Runner…

QT 官方例程阅读: XML Patterns 相关

标签用于在qt creator 中查询相关工程 一、标签 Schema Validator 模式验证器 就是根据 已知的XML 模式&#xff0c;验证输入的XML 文件格式是否匹配&#xff0c;不匹配可以输出不匹配位置 如下&#xff0c;&#xff0c;首先定义了contact 元素 的子元素列表&#xff0c;&…

【Redis】list以及他的应用场景

介绍 &#xff1a;list 即是 链表。链表是一种非常常见的数据结构&#xff0c;特点是易于数据元素的插入和删除并且且可以灵活调整链表长度&#xff0c;但是链表的随机访问困难。许多高级编程语言都内置了链表的实现比如 Java 中的 LinkedList&#xff0c;但是 C 语言并没有实现…

64、ubuntu使用c++/python调用alliedvisio工业相机

基本思想&#xff1a;需要使用linux系统调用alliedvisio工业相机完成业务&#xff0c;这里只做驱动相机调用&#xff0c;具体不涉及业务开发 Alvium 相机选型 - Allied Vision 一、先用软件调用一下用于机器视觉和嵌入式视觉的Vimba X 软件开发包 - Allied Vision VimbaX_Set…

解决在pycharm中无法进入conda环境的问题

问题原因&#xff1a; pycharm中使用的是Windows PowerShell 解决方法&#xff1a; setting -> Terminal中将shell path修改为win的即可--注意需要重启

Java技术栈 —— 手写Java数据库连接池

Java技术栈 —— 手写Java数据库连接池 一、连接池的作用二、讲解1.1 类图结构1.2 ConnectionPoolManager1.3 DataSourceConfig1.4 ConnectionPool与IConnectionPool1.5 ConnEntry 三、收获3.1 CopyOnWriteArrayList累的使用(对本文代码的一点建议和指正)3.2 AtomicInteger类的…

【嵌入式学习】C++QT-Day2-C++基础

笔记 见我的博客&#xff1a;https://lingjun.life/wiki/EmbeddedNote/19Cpp 作业 自己封装一个矩形类(Rect)&#xff0c;拥有私有属性:宽度(width)、高度(height)&#xff0c; 定义公有成员函数: 初始化函数:void init(int w, int h) 更改宽度的函数:set_w(int w) 更改高度…

css display 左右对齐 技巧

.list_number{ display: flex; } .list_name_number{ width:100px; } //左边固定width .list_name_type{ //右边给flex:2 自动撑开 flex:2; }

使用antd design pro 如何设置不使用全局基础模板,开发开放公共页面。

修改config目录下的routes&#xff0c; 在指定需要开放不使用全局模版的路径&#xff0c;多个路径可单独添加或者直接按照分级添加模式&#xff1a; 这样添加了还不行&#xff0c;因为模版本身除了user模块以外&#xff0c;其他路径都需要登陆后才能访问&#xff0c;但一般做p…

《微信小程序开发从入门到实战》学习九十三

7.1 视图容器组件 7.1.3 swiper与swiper-item组件 swiper组件的显示效果如下图所示&#xff1a; indicator-dots、indicator-color和indicator-active-color三个属性用于设置swiper组件下方的指示点。设置指示点的颜色时&#xff0c;可以使用HexColor&#xff0c;也可以使用r…

力扣算法-Day18

18.四数之和 给你一个由 n 个整数组成的数组 nums &#xff0c;和一个目标值 target 。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]] &#xff08;若两个四元组元素一一对应&#xff0c;则认为两个四元组重复&#xff09;&#xff1…

linux 查找文件或查找内容 (find grep)

一 linux 查找包含指定内容的文件&#xff1a; 在linux 有时我们只我知道内容但不知道文件在哪&#xff0c;可以使用find 与grep查找 例1 要查找指定目录&#xff08;默认包含子目录&#xff09;文件内容包含 xxx 的文件 find /etc/ -type f -exec grep -l "mysql"…

通过一个 Spring 的 HelloWorld 引入 Spring 要点

目录 一. 前言 二. 设计一个 Spring 的 HelloWorld 2.1. 创建 HelloWorld 项目 2.2. 核心要点一&#xff1a;控制反转&#xff08;IOC&#xff09; 2.3. 核心要点二&#xff1a;面向切面&#xff08;AOP&#xff09; 三. Spring 框架如何逐步简化开发 3.1. Java 配置方式…