C++中使用vector保存新建对象中自指指针的问题

news2025/1/24 5:45:07

问题

在某些场景中(例如并查集),我们需要将新建对象中的指针指向对象自己。例如,

struct factor {
	int data;
	factor* next;

	factor(int i) : data(i), next(this){}
};

    这样的结构体当然没有问题,如果我们想以类似链表的形式将元素串联起来,并且找到链表的头部(也就是next指针指向自己的元素)。

其中num1->num2,num2->num3,num3->num4,num4->num4。然后打印num4的值,得到结果4。到目前为止,进行的很顺利,没有意外发生。

如果我们不想手动挨个创建factor对象,而是将他们保存在STL容器中。可以这样做

vector<factor> factor_array;
vector<int> nums = { 1,2,3,4,5,6 };

for (int i = 0; i < nums.size(); i++) {
	factor_array.emplace_back(nums[i]);
}

factor_array[0].next = &factor_array[2];
factor_array[2].next = &factor_array[1];
factor_array[1].next = &factor_array[3];

factor* p = &factor_array[0];
while (p->next != p) {
	p = p->next;
}

cout << p->data;

编译,运行!

哦嚯,出问题了。

首先排除是我们的factor类写的不对,因为我们已经验证过,手动创建每个对象时的逻辑是正确的。

那么是不是emplace_back的问题呢?将emplace_back改成push_back试一试。

成功得到结果。但是这个结果有点不对劲,0指向2,2指向1,1指向3,3初始化指向自己,为什么最后结果是6呢?

解决问题

这样,我们将每一个创建后的对象结果打印出来。

	vector<factor> factor_array;
	vector<int> nums = { 1,2,3,4,5,6 };

	for (int i = 0; i < nums.size(); i++) {
		factor_array.push_back(nums[i]);
		for (int j = 0; j < factor_array.size(); j++) {
			cout << factor_array[j].data << "'s next points at " << factor_array[j].next << "  " << factor_array[j].next->data << endl;
		}
		cout << endl;
	}

	factor_array[0].next = &factor_array[2];
	factor_array[2].next = &factor_array[1];
	factor_array[1].next = &factor_array[3];

	factor* p = &factor_array[0];
	while (p->next != p) {
		p = p->next;
	}

	cout << p->data;

这样,我们发现了问题的所在。每次创建对象时,this指向的位置是相同的!这与push_back和emplace_back的实现原理有关,我们后面展开说。

我们进一步获取更详细的信息,

struct factor {
	int data;
	factor* next;

	factor(int i) : data(i), next(this) { cout << data << " created at " << this << endl; }
};

在factor的构造函数中打印出当前对象是在哪块内存中创建的。

	vector<factor> factor_array;
	vector<int> nums = { 1,2,3,4,5,6 };

	for (int i = 0; i < nums.size(); i++) {
		factor_array.push_back(nums[i]);
		cout << factor_array[i].data << " at " << &factor_array[i] << " next is  " << factor_array[i].next << endl;
	}
	cout << endl;

	for (int j = 0; j < factor_array.size(); j++) {
		cout << factor_array[j].data << " at " << &factor_array[j] << " next is  " << factor_array[j].next << endl;
	}
	cout << endl;

	factor_array[0].next = &factor_array[2];
	factor_array[2].next = &factor_array[1];
	factor_array[1].next = &factor_array[3];

	factor* p = &factor_array[0];
	while (p->next != p) {
		p = p->next;
	}

	cout << p->data;

比较一下创建前后对象在内存中的位置。我们得到结果为

现在的情况我们分析一下,在vector在创建我们的对象时,所有对象的创建都是在一块空间内完成的。根据我们factor的构造函数来看,next指针理所当然的指向了创建对象的那块空间。后续过程vector将创建好的对象复制到真正保存数据的内存中,然而,所有对象的next指针依然指向创建时的内存地址。该地址在vector创建对象结束后被释放,但是并没有被其他程序改写,所以factor* p 依然能从中读取到最后创建的对象的信息,也就是6。

所以,如果想让vector中保存的对象中的自指指针指向自己。需要在vector创建后,遍历一次每个对象,使其指针自指。

使用vector保存带有指向自身或者指向其他保存在同一容器中的其他对象的指针的对象时,在整个vector初始化后,不要再向其中添加其他新的元素。

因为在vector已经开辟的空间使用完毕后,再加入新的元素需要重新开辟空间,并且将整个“对象数组”复制过去。此时依然会发生指针指向的位置与实际对象位置不匹配的错误。

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

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

相关文章

VSCode 正则表达式 匹配多行

VS Code 正则表达式匹配多行 (.|\n)*? //test.js const test {str: VS Code 正则表达式匹配多行VS Code 正则表达式匹配多行VS Code 正则表达式匹配多行VS Code 正则表达式匹配多行VS Code 正则表达式匹配多行VS Code 正则表达式匹配多行VS Code 正则表达式匹配多行VS Code …

第 5 章 栈

文章目录 5.1 栈的一个实际需求5.2 栈的介绍5.3 栈的应用场景5.4 栈的快速入门5.5 栈实现综合计算器(中缀表达式)5.6 逆波兰计算器5.7 中缀表达式转换为后缀表达式5.7.1 具体步骤如下5.7.2 举例说明5.7.3 代码实现中缀表达式转为后缀表达式 5.8 逆波兰计算器完整版5.8.1 完整版…

Vue-路由-声明式导航

1. 导航链接 vue-router 提供了一个全局组件 router-link (取代 a 标签) 能跳转&#xff0c;配置 to 属性指定路径(必须) 。本质还是 a 标签 &#xff0c;to 无需 #能高亮&#xff0c;默认就会提供高亮类名&#xff0c;可以直接设置高亮样式 如&#xff1a; <div class&…

openGauss学习笔记-192 openGauss 数据库运维-常见故障定位案例-XFS文件系统问题

文章目录 openGauss学习笔记-192 openGauss 数据库运维-常见故障定位案例-XFS文件系统问题192.1 在XFS文件系统中&#xff0c;使用du命令查询数据文件大小大于文件实际大小192.1.1 问题现象192.1.2 原因分析192.1.3 处理办法 192.2 在XFS文件系统中&#xff0c;出现文件损坏192…

javacv和opencv对图文视频编辑-常见错误汇总

JavaCV 是一个使用 Java 语言实现的 OpenCV 和 FFmpeg 的接口库&#xff0c;它允许 Java 开发人员使用这些强大的多媒体处理库。然而&#xff0c;在使用 JavaCV 进行开发时&#xff0c;可能会遇到一些常见的错误。在本文中&#xff0c;我将介绍一些常见的 JavaCV 错误和解决方法…

文件操作(二)

͟͟͞͞&#x1f3c0;前言上一篇我们加们讲了什么是文件&#xff0c;为什么使用文件&#xff0c;以及流的概念。我们继续接上一篇来继续讲解我们的文件操作&#xff0c;这一篇将会详细的讲如何对文件进行读写。 目录 &#x1f680;一.文件的顺序读写 1.fgetc和fputc 2.fget…

Redis未授权访问漏洞复现与工具安装

目录 一、漏洞简介 二、靶场搭建 三、漏洞检测 四、工具安装 五、远程连接 六、利用Redis写入webshell 七、redis-getShell工具 八、ssh公私钥免密登录 九、其他 一、漏洞简介 redis是一个数据库&#xff0c;默认端口是6379&#xff0c;redis默认是没有密码验证的&…

16 张动图讲透网络原理

网络其实存在于日常生活中的每一个角落。 你的电脑&#xff0c;打印机&#xff0c;手机&#xff0c;甚至电视等等都属于网络设备。通常&#xff0c;你需要将这些设备通过网络连接起来&#xff0c;这样就可以实现数据的传输和共享&#xff0c;让工作生活更加便捷。 如果你的连接…

模型索引:QModelIndex

一、为什么要使用模型索引&#xff1f; 从名字可以看出&#xff0c;他是模型的索引&#xff0c;只要对模型实体&#xff08;各种xxxModel的实体&#xff09;施加这个索引&#xff0c;model就会返回数据集中对应的值&#xff0c;或者通过这个索引修改对应数据集中的值。 类比数…

【linux】软链接创建(linux的快捷方式创建)

软连接的概念 类似于windows系统中的快捷方式。有的文件目录很长或者每次使用都要找很不方便&#xff0c;于是可以用类似windows的快捷方式的软链接在home&#xff08;初始目录类似于桌面&#xff09;上创建一些软链接方便使用。 软链接的语法 ln -s 参数1 参数2 参数1&#…

GitHub图床搭建

1 准备Github账号 如果没有Github账号需要先在官网注册一个账号 2 创建仓库 在github上创建一个仓库&#xff0c;随便一个普通的仓库就行&#xff0c;选择公共仓库 3 github token获取 github token创建方式可以参考下面的方式&#xff1a; https://www.xichangyou.com/6…

树及二叉树

文章目录 树的概念和结构树的概念树的相关概念 二叉树的概念和结构概念特殊的二叉树二叉树的性质二叉树的存储结构顺序存储链式存储 二叉树的顺序结构及实现二叉树的顺序结构堆的概念及结构 二叉树链式结构的实现二叉树的遍历 树的概念和结构 树的概念 树是一种非线性的数据结…

Vulnhub-DC1

前言 一个比较简单的实战靶场&#xff0c;官方要求是找到/root下的flag&#xff0c;所以直接提权即可。但对于学习和训练来说还是太简略了&#xff0c;在打靶场的时候还是全面一些较好。 本次靶场实战涉及信息收集、漏洞查找与利用、getshell、数据库渗透、密码破解、linux提…

京东001号快递员,刘强东给的买房承诺,仅4年时间就实现了

京东001号快递员金宜财正式退休&#xff0c;不知是有意还是无意&#xff0c;他成为了互联网的热搜&#xff0c;不过大众更关心的是金宜财当年得到刘强东的承诺&#xff0c;只要干几年就能买房&#xff0c;那么刘强东的承诺有没有实现&#xff0c;又是如何实现的呢&#xff1f; …

【Vue3】2-11 : 生命周期钩子函数及原理分析

本书目录&#xff1a;点击进入 一、组件生命周期概述 1.1 官方生命周期 1.2 钩子函数&#xff08;回调函数&#xff09; ▶ 生命周期可划分为三个部分(- >表示执行循序)&#xff1a; 二、实战&#xff1a;测试生命周期流程 &#xff1e; 代码 &#xff1e; 效果 一…

软件测试|如何使用Python取句中中英文内容

简介 提取句子中的中英文内容在自然语言处理中是一个常见任务&#xff0c;通常用于文本处理、机器翻译和语言学研究。本文将详细介绍如何使用 Python 提取句子中的中英文内容&#xff0c;包括准备工作、选择合适的库、编写代码示例和演示示例。 准备工作 我们可以使用Python…

ruoyi后台管理系统部署-4-安装nginx

yum 安装 ngix 1.24 yum 官方源安装&#xff1a; # 1. 需要预先安装 yum-utils sudo yum install yum-utils # 2. 配置yum repo touch /etc/yum.repos.d/nginx.repongix.repo: [nginx-stable] namenginx stable repo baseurlhttp://nginx.org/packages/centos/$releasever/$…

【26 预处理详解】

目录 预定义符号#define定义常量#define定义宏带有副作用的宏参数宏替换的规则宏函数的对比#和##命名约定#undef命令行定义条件编译头文件的包含其他预处理指令 1. 预定义符号 c语言设置了一些预定义符号&#xff0c;可以直接使用&#xff0c;预定义符号也是在预处理期间处理…

江科大STM32 下

目录 7、ADC数模转换器8、DMA直接存储器存取9、USART串口9-2 串口发送接受9-3 串口收发HEX数据包 I2CSPI协议10.1 SPI简介W25Q64简介10.3 SPI软件读写W25Q6410.4 SPI硬件读写W25Q64 BKP、RTC11.0 Unix时间戳11.1 读写备份寄存器BKP11.2 RTC实时时钟 十二、PWR12.1 PWR简介12.2 …

Java21 + SpringBoot3集成WebSocket

文章目录 前言相关技术简介什么是WebSocketWebSocket的原理WebSocket与HTTP协议的关系WebSocket优点WebSocket应用场景 实现方式1. 添加maven依赖2. 添加WebSocket配置类&#xff0c;定义ServerEndpointExporter Bean3. 定义WebSocket Endpoint4. 前端创建WebSocket对象 总结 前…