75 C++对象模型探索。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
}

基类和子类中有虚函数表

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

总结

1.虚函数表是跟着类走的。


2.虚函数指针是跟着对象走的。


3.一个类只有包含了虚函数才会存在虚函数表,同属于一个类的对象共享虚函数表,但是有各自的vptr。


4.父类中有虚函数就等于子类中有虚函数,因为子类会继承父类的虚函数。换句话说,父类中有虚函数表,则子类中也有虚函数表。


5.父类和子类会都各自拥有一个虚函数表,子类如果有不同的实现,则会在这个子类的虚函数表中更改。如果没有,则子类的虚函数表和父类的内容一样。注意,仅仅是内容相同,但 不是同一个表,可以理解为:在内存中的不同位置的,内容相同的两张表


6. 如果使用子类对象给父类对象值,子类中的属于父类的那部分内容会被编译器自动区分出来并拷贝给父类对象。


Son son;


Base base = son; //这一行实际上干了如下两件事情


a.生成一个base对象,base生成自己的虚函数指针。


b.将son中父类的部分赋值给base


c.注意:son的虚函数指针并没有覆盖 base的虚函数指针


那么有没有一种情况,就是子类可能有多个虚函数表?TODO

C++是 面向对象 和 基于对象 的概念

面向对象:让父类指针或者父类引用指向子类对象,通过虚函数实现,这就是常说的OO(object-oriented model),支持多态。

OB(object-based),也叫ADT抽象数据模型【abstract datdatype model】,不支持多态,执行速度更快,因为函数调用的解析不需要运行时决定。

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

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

相关文章

AI Agents系列—— 探究大模型的推理能力,关于Chain-of-Thought的那些事儿

一、写在前面&#xff1a;关于AI Agents与CoT 本文是2023.07.24发表在同名公众号「陌北有棵树」上的一篇文章&#xff0c;个人观点是基础理论的学习现在仍是有必要的&#xff0c;所以搬运过来。 今天要读的论文是《Chain-of-Thought Prompting Elicits Reasoning in Large La…

【C++】C++入门基础讲解(一)

&#x1f497;个人主页&#x1f497; ⭐个人专栏——C学习⭐ &#x1f4ab;点击关注&#x1f929;一起学习C语言&#x1f4af;&#x1f4ab; 导读 经过一段时间的C语言学习&#xff0c;我们以及基本掌握了C语言的知识&#xff0c;今天&#xff0c;我们就开始学习C&#xff0c;…

张维迎《博弈与社会》笔记(2)导论:个体理性与社会最优:协调与合作问题

有节选&#xff0c;相当于按照自己的方式将内容组织了下吧&#xff1f; 协调与合作问题 什么是一个社会面临的基本问题&#xff1f; 这似乎是一个我们每一个人都可以触摸得到但又难以说清的问题&#xff0c;因为在不同的语境下&#xff0c;“社会”一词的内涵有所差异。但其基…

如何阅读xml电子发票

xml电子发票是官方给出的电子存档的文件格式&#xff0c;本质是文本&#xff0c;所以文件很小&#xff0c;大量发票存储&#xff0c;能够更加凸显优势。 但是xml电子发票不方便阅读&#xff0c;因为里面是xml格式&#xff0c;对于财务人员来讲&#xff0c;看“代码”简直太难了…

网络安全防御保护实验(二)

一、登录进防火墙的web控制页面进行配置安全策略 登录到Web控制页面&#xff1a; 打开Web浏览器&#xff0c;输入防火墙的IP地址或主机名&#xff0c;然后使用正确的用户名和密码登录到防火墙的Web管理界面。通常&#xff0c;这些信息在防火墙设备的文档或设备上会有说明。 导…

【创建vue项目的两种方式】

Vue环境搭建 NodeJs安装包安装淘宝镜像 环境搭建webpack安装全局安装vue/cli查看模板创建项目1.webpack2. vue-cli NodeJs安装包 下载链接&#xff1a;官网链接 下载下来后&#xff0c;直接傻瓜式的安装即可。 通过在cmd控制台输入以下命令查看是否安装成功 node -v因为适配某…

如何使用WinSCP公网远程访问本地CentOS服务器编辑上传文件

文章目录 1. 简介2. 软件下载安装&#xff1a;3. SSH链接服务器4. WinSCP使用公网TCP地址链接本地服务器5. WinSCP使用固定公网TCP地址访问服务器 1. 简介 ​ Winscp是一个支持SSH(Secure SHell)的可视化SCP(Secure Copy)文件传输软件&#xff0c;它的主要功能是在本地与远程计…

利用ADS建立MIPI D-PHY链路仿真流程

根据MIPI D-PHY v1.2规范中对于互连电气参数的定义,本次仿真实例中,需要重点关注如下的设计参数: 1. 差分信号的插入损耗Sddij和回拨损耗Sddii; 2. 模式转换损耗Sdcxx、Scdxx; 3. 数据线与时钟线之间的串扰耦合(远、近端)。 设计者还可以结合CTS中的补充…

Mediasoup Demo-v3笔记(五)——Mediasoup 的启动

Mediasoup是由两部分组成的&#xff0c;一部分是js的控制模块&#xff0c;一部分是c的传输模块&#xff0c;在这里我们用mediasoup demo的代码开始&#xff0c;分析整个进程的启动过程 1、在mediasoup-demo-3的server.js中&#xff0c;调用启动方法 mediasoup-demo-3是一个dem…

快乐学Python,DataFrame的基本操作

在上一篇文章中&#xff0c;我们了解了如何使用 pandas 的函数来从多种数据源&#xff1a;csv、excel 和 html 网页。其中不管是哪一种数据读取的方式&#xff0c;最终返回的都是一个 DataFrame 对象。 对于 DataFrame 对象&#xff0c;我们只是简单将其打印出来&#xff0c;这…

四、MyBatis 动态语句

本章概要 动态语句需求和简介if 和 where 标签set 标签trim 标签(了解)choose/when/otherwise 标签foreach 标签sql 片段 4.1 动态语句需求和简介 经常遇到很多按照很多查询条件进行查询的情况&#xff0c;比如智联招聘的职位搜索等。其中经常出现很多条件不取值的情况&#…

【Linux】Linux进度条小程序(包含色块实现)

我们再将Linux常用工具与命令都学会了之后&#xff0c; 设计进度条这个小程序可以比较好的帮助我们进行一定程度练习与巩固 目录 预备知识&#xff1a;回车换行&#xff1a;缓冲区&#xff1a; 进度条&#xff1a;准备工作&#xff1a;主题思路&#xff1a;代码实现&#xff1a…

腾讯云服务器上发送邮件连接超时

腾讯云会将服务器25端口禁用&#xff08;腾讯云默认禁用&#xff09;&#xff0c;开启后发送正常 https://console.cloud.tencent.com/secctrl 参考 腾讯云服务器上发送邮件连接超时&#xff08;无法发送&#xff09;的相关问题

spring-boot-starter-validation常用注解

文章目录 一、使用二、常用注解三、Valid or Validated &#xff1f;四、分组校验1. 分组校验的基本概念2. 定义验证组3. 应用分组到模型4. 在控制器中使用分组5. 总结 一、使用 要使用这些注解&#xff0c;首先确保在你的 Spring Boot 应用的 pom.xml 文件中添加了 spring-bo…

使用EtherNET转Profinet网关配置EtherNET/IP地址说明

EtherNET转Profinet网关配置EtherNET/IP地址是将两种网络之间的连接进行设置和调整&#xff0c;以便实现数据的传输和信息的交互。这个过程中&#xff0c;需要对EtherNET/IP地址进行配置&#xff0c;以确保数据能够正确地在网络之间传递。通过配置EtherNET/IP地址&#xff0c;可…

vue2(Vuex)、vue3(Pinia)、react(Redux)状态管理

vue2状态管理Vuex Vuex 是一个专为 Vue.js应用程序开发的状态管理模式。它使用集中式存储管理应用的所有组件的状态&#xff0c;以及规则保证状态只能按照规定的方式进行修改。 State&#xff08;状态&#xff09;:Vuex 使用单一状态树&#xff0c;即一个对象包含全部的应用层…

sql管理工具archery简介

在平时的工作过程中&#xff0c;我们肯定会遇到使用sql平台的场景&#xff0c;业内也有很多工具&#xff0c;类似阿里云的dms&#xff0c;但是这个是和云厂商绑定的&#xff0c;我们可能一般没有用到阿里云组件就比较困难了&#xff0c;那还有什么选项了&#xff0c;经过调研&a…

JVM简介

一、什么是JVM JVM是Java Virtual Machine&#xff08;Java虚拟机&#xff09;的缩写&#xff0c;JVM是一种用于计算设备的规范&#xff0c;它是一个虚构出来的计算机&#xff0c;是通过在实际的计算机上仿真模拟各种计算机功能来实现的。Java虚拟机包括一套字节码指令集、一组…

YOLOv5全网独家首发:Powerful-IoU更好、更快的收敛IoU,效果秒杀CIoU、GIoU等 | 2024年最新IoU

💡💡💡本文独家改进:Powerful-IoU更好、更快的收敛IoU,是一种结合了目标尺寸自适应惩罚因子和基于锚框质量的梯度调节函数的损失函数 💡💡💡MS COCO和PASCAL VOC数据集实现涨点 收录 YOLOv5原创自研 https://blog.csdn.net/m0_63774211/category_1251193…

哪些 3D 建模软件值得推荐?

云端地球是一款免费的在线实景三维建模软件&#xff0c;不需要复杂的技巧&#xff0c;只要需要手机&#xff0c;多拍几张照片&#xff0c;就可以得到完整的三维模型&#xff01; 无论是大场景倾斜摄影测量还是小场景、小物体建模&#xff0c;都可以通过云端地球将二维数据向三…
最新文章