C++的封装(十三):迭代器问题

前面讨论了linux风格的链表的做法。那个例子没有用到迭代器。现在把它加上:

class list {
public:struct node {pair<node*, node*>link;} handle;list() { handle.link.first=handle.link.second=&handle; }~list() {}public:struct iterator_st {node *p;};class iterator : iterator_st{public:iterator operator++();iterator operator++(int);iterator operator--();iterator operator--(int) ;bool operator==(iterator it);};iterator begin();iterator end();
};

list部分的代码这里就省略了。参见前文<<C++的封装(十一):linux风格的链表和稀疏矩阵>>https://blog.csdn.net/aaasssdddd96/article/details/139167455。

list需要创建begin()迭代器和end()迭代器。这两个是iterator类逻辑上的构造函数。这就是iterator的对象工厂了。这部分内容在前文<<C++的封装(十二):外部构造函数>>https://blog.csdn.net/aaasssdddd96/article/details/139551253也讨论过了。list创建迭代器对象需要访问iterator的私有数据,所以iterator类应当声明list为友元。自然,友元不是唯一的办法,只要能解决好访问的问题,友元不友元无所谓了。这里打算用前文<<C++的封装(十):数据和代码分离>>中讨论的方法来处理这个问题https://blog.csdn.net/aaasssdddd96/article/details/137865098。所以让class iterator 继承了struct iterator_st。这里想说明,在别人的代码里,看到代码不是自己想象的样子,也不用奇怪,因为有各种不同的实现方法。

有了这些之后,就可以给出一个客户化后的遍历的例子:

struct node {int x;list::node node;static struct node *recast(list::node *p) {struct node *q;list::node node::*r= &node::node;reinterpret_cast<int &>(q)=reinterpret_cast<int >(p)-reinterpret_cast<int&>(r);return q;}
};void disp(list &l)
{list::iterator it;node *p;for(it=l.begin(); it!=l.end(); it++){p= node::recast((list::node*&)it);printf("%d ", p->x);}printf("End.\n");
}

disp()函数中有个iterator不等于的比较。iterator类重载operator!=()是第一反应。但这里不想这么做。因为iterator的对象太简单了,里面只有一个基本指针,如果做成成员函数,无论如何都要传一个this指针,然后婉转的通过指针引用iterator对象。这里想做成值传递,因为值传递更有效。这样在全局重载operator!=运算符。这个全局的operator!=需要访问iterator的私有数据,所以应当在iterator类中声明它是友元。和前面同样的道理,这里也没有。

typedef list::iterator iterator;
bool operator!=(iterator i, iterator t) 
{typedef list::iterator_st iterator_st;return ((iterator_st&)i).p!=((iterator_st&)t).p;
}

这些代码加到一起,就可以跑一下了:

#include <stdio.h>
#include <utility>using std::pair;class list {
public:struct node {pair<node*, node*>link;} handle;list() { handle.link.first=handle.link.second=&handle; }~list() {}public:struct iterator_st {node *p;};class iterator : iterator_st{public:iterator operator++() { p=p->link.first; return *this; }iterator operator++(int) {iterator q=*this; p=p->link.first; return q;}iterator operator--() { p=p->link.second; return *this; }iterator operator--(int) {iterator q=*this; p=p->link.second; return q;}bool operator==(iterator it) {return p==it.p;}};iterator begin() {iterator i; ((iterator_st&)i).p=handle.link.first; return i;}iterator end() {iterator i; ((iterator_st&)i).p=&handle; return i;}
};typedef list::iterator iterator;
bool operator!=(iterator i, iterator t) {return i!=t;}struct node {int x;list::node node;static struct node *recast(list::node *p) {struct node *q;list::node node::*r= &node::node;reinterpret_cast<int &>(q)=reinterpret_cast<int >(p)-reinterpret_cast<int&>(r);return q;}
};void disp(list &l)
{list::iterator it;node *p;for(it=l.begin(); it!=l.end(); it++){p= node::recast((list::node*&)it);printf("%d ", p->x);}printf("End.\n");
}int main()
{list l;disp(l);return 0;
}

刚开始的版本,这里的operator!=代码不小心犯了个错误。因为默认的复制构造函数和赋值函数按位拷贝。顺理成章的误以为代码中的i!=t也会按位比较(实际是递归调用自己)。而编译器也没有报错。这样一run立刻就crash了。

调试看到错误发生在disp()函数中。为了搞清楚究竟发生了什么,用-S编译选项生成它的汇编代码:

LC0:.ascii "%d \0"
LC1:.ascii "End.\12\0".text.align 2
.globl __Z4dispR4list.def    __Z4dispR4list; .scl    2;      .type   32;     .endef
__Z4dispR4list:pushl   %ebpmovl    %esp, %ebpsubl    $24, %espmovl    8(%ebp), %eaxmovl    %eax, (%esp)call    __ZN4list5beginEvmovl    %eax, -12(%ebp)movl    -12(%ebp), %eaxmovl    %eax, -4(%ebp)
L4:movl    8(%ebp), %eaxmovl    %eax, (%esp)call    __ZN4list3endEvmovl    %eax, -16(%ebp)movl    -16(%ebp), %eaxmovl    %eax, 4(%esp)movl    -4(%ebp), %eaxmovl    %eax, (%esp)call    __ZneN4list8iteratorES0_testb   %al, %alje      L5movl    -4(%ebp), %eaxmovl    %eax, (%esp)call    __ZN4node6recastEPN4list4nodeEmovl    %eax, -8(%ebp)movl    -8(%ebp), %eaxmovl    (%eax), %eaxmovl    %eax, 4(%esp)movl    $LC0, (%esp)call    _printfmovl    $0, 4(%esp)leal    -4(%ebp), %eaxmovl    %eax, (%esp)call    __ZN4list8iteratorppEijmp     L4
L5:movl    $LC1, (%esp)call    _printfleaveret

汇编代码看起来虽然比较吃力,但还是可以看到,disp()函数调用了__ZN4list5beginEv函数,对应源代码的list::begin(),然后调用了 __ZN4list3endEv函数,对应源代码的list::end(),然后又调用了 _ZneN4list8iteratorES0,它对应源代码的operator!=。

继续察看__ZneN4list8iteratorES0_的代码:

.globl __ZneN4list8iteratorES0_.def    __ZneN4list8iteratorES0_;       .scl    2;      .type   32;
.endef
__ZneN4list8iteratorES0_:pushl   %ebpmovl    %esp, %ebpsubl    $8, %espmovl    12(%ebp), %eaxmovl    %eax, 4(%esp)movl    8(%ebp), %eaxmovl    %eax, (%esp)call    __ZneN4list8iteratorES0_movzbl  %al, %eaxleaveret

它传完参数直接调用了自己!所以陷入无限递归了。operator!=中的i!=t递归调用了自己。这是造成crash的原因。哈哈,写的高兴就失误了。找到原因后改正就容易了,就是用((iterator_st&)i).p!=((iterator_st&)t).p来替换i!=t 表达式。这样就成了。

顺便说一下,察看生成的汇编代码时,因为编译器对原函数作了名称转化,运算符重载函数名不易辨认,可在源代码挨着运算符重载换个名字再写一下这个代码,这样在汇编代码中就容易找了。

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

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

相关文章

PaddleOCR学习——PP-OCR系列

相关知识前置&#xff1a; PP-LCNet PP-LCNetV3 PP-LCNetV3系列模型是PP-LCNet系列模型的延续&#xff0c;覆盖了更大的精度范围&#xff0c;能够适应不同下游任务的需要。PP-LCNetV3系列模型从多个方面进行了优化&#xff0c;提出了可学习仿射变换模块&#xff0c;对重参数…

webpack之HMR

什么是HMR Hot Module Replacement是指当我们对代码修改并保存后&#xff0c;webpack将会对代码进行重新打包&#xff0c;并将新的模块发送到浏览器端&#xff0c;浏览器用新的模块替换掉旧的模块&#xff0c;以实现在不刷新浏览器的前提下更新页面 使用HMR 安装 yarn add …

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

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

easyexcel的简单使用(execl模板导出)

模板支持功能点 支持列表支持自定义头名称支持自定义fileName支持汇总 模板示例 操作 pom引入 <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>${easyexcel.version}</version></dep…

uniapp中u-input点击事件失效

当给u-input设置了disabled/readonly属性后&#xff0c;pc浏览器中点击事件失效&#xff0c;但是app/移动端h5中却仍有效 解决办法 给外边包上一个盒子设置点击事件&#xff0c;给input加上css属性&#xff1a;pointer-events&#xff1a;none pointer-events CSS 属性指定在什…

糖料蔗精准测产 ,珈和科技倾力打造广西农险科技服务新标杆!

2024年中央一号文件中提到&#xff0c;鼓励地方发展特色农产品保险。随着近年来广西省对农业风险管理方式的不断探索与试点&#xff0c;糖料蔗、桉树、柑橘等种植收入保险需求迅速增加&#xff0c;传统的测产方式在效率上不仅耗时费力&#xff0c;而且难以满足大规模经济作物的…

Vue笔记(三)

上一篇&#xff1a;Vue二&#xff09;-CSDN博客 目录 1.自定义指令 v-loading的封装 2.插槽 文本插槽 文本插槽&#xff08;有默认值&#xff09; 具名插槽 作用域插槽 详细做一个练习 实现如下效果 目录结构 准备数据 父传子数据 使用文本插槽自定义按钮文本 实…

RushJs遇到Browserslist: caniuse-lite is outdated解决方案

突然。 CI服务器上打包应用的是会报一个警告。 Browserslist: caniuse-lite is outdated. Please run: npx update-browserslist-dblatest Why you should do it regularly: https://github.com/browserslist/update-db#readmeBrowserslist里的数据库过期了。 这个警告会让C…

Maven: 编码GBK的不可映射字符不能编译

使用mvn compile命令&#xff0c;出现错误: 编码GBK的不可映射字符不能编译。这是因为代码或注释中存在中文引起的&#xff0c;一般在ide中会自动处理编译时的字符集&#xff0c;就不会碰到这个错误。这个错误是在生成代码后&#xff0c;其中自动加上了中 文注释&#xff0c;手…

vue路由:hash模式下跳转404的问题。

vue的路由模式&#xff1a;hash和history。 最近有个问题&#xff0c;其实很常见就是hash模式跳转404。 发生问题的场景&#xff1a; 1.项目加载的动态路由。 2.首页和登录页在一个动态路由模块&#xff0c;而其他的管理层页面模块在另外一个动态路由模块。按需加载&#x…

在两个脚本之间传输文本信息

一、接收方有一个文本&#xff0c;用于显示接收的信息 (一&#xff09;接收方&#xff1a; 1、UI-Text TextMeshPro&#xff0c;名字自取。如&#xff1a;TitleText 2、给TitleText添加Recipients.cs组件。 using System.Collections; using System.Collections.Generic; u…

动态防护开启教程和体验感受

动态防护是雷池 WAF 社区版在版本 [6.0.0] 中新增的一个功能&#xff0c;它属于站点高级防护的一部分。动态防护的主要作用是自动动态加密网站的 HTML 和 JavaScript 源码&#xff0c;目的是阻止爬虫和攻击自动化程序的分析。这项功能在 [6.0.0] 版本中标记为 BETA 版本&#x…

c#中switch case语句的用法

前言 在c#中如果对于两种不同情况的处理&#xff0c;一般使用If else结构&#xff0c;但是对于3种及以上情况的处理最好使用switch case语句来增强代码的可读性&#xff0c;本文就是详细介绍switch case语句在c#中的常见应用 1、常规使用 下面的代码中c是一个Int类型的变量&…

2024 年最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)

OpenAi 环境安装 首先确保您的计算机上已经安装了 Python。您可以从 Python 官方网站下载并安装最新版本 Python。安装时&#xff0c;请确保勾选 “Add Python to PATH” &#xff08;添加环境变量&#xff09;选项&#xff0c;以便在 cmd 命令行中直接使用 Python。 安装 Op…

华为大咖说 | AI 是行业的未来, 还是另一个“元宇宙”?

本文作者&#xff1a;陈冠宏&#xff08;华为网络MSSD首席顾问&#xff09;全文约4497字&#xff0c;阅读约需10分钟 在本年度的517电信日上&#xff0c;中国电信高层在产品升级计划发布会中喊出“ALL in AI”战略&#xff0c;其震撼力让人瞩目。 自2022年11月OpenAI推出划时代…

机器学习归一化特征编码

特征缩放 因为对于大多数的机器学习算法和优化算法来说&#xff0c;将特征值缩放到相同区间可以使得获取性能更好的模型。就梯度下降算法而言&#xff0c;例如有两个不同的特征&#xff0c;第一个特征的取值范围为1——10&#xff0c;第二个特征的取值范围为1——10000。在梯度…

Facebook隐私保护:用户数据安全的挑战与应对策略

在当今数字化时代&#xff0c;随着社交媒体的普及和信息技术的快速发展&#xff0c;人们对于个人数据隐私和安全的关注越来越高。作为全球最大的社交网络平台之一&#xff0c;Facebook在用户数据保护方面面临着诸多挑战和责任。本文将深入探讨Facebook在隐私保护方面的现状、面…

k8s+springcloud+nacos部署配置

1 k8s 部署nacos-2.1.2配置k8s-nacos-statefulSet.yaml文件 apiVersion: v1 kind: Service metadata:name: nacos-headlessnamespace: rz-dtlabels:app: nacosannotations:service.alpha.kubernetes.io/tolerate-unready-endpoints: "true" spec:# 3个端口打开&…

拓扑排序和关键路径详解

目录 拓扑排序 关键路径 拓扑排序 如果有一个有向图的任意顶点都无法通过一些有向边回到身边&#xff0c;那么称这个有向图为有向无环图。 拓扑排序是将有向无环图的所有顶点排成一个线性序列&#xff0c;使得对图中的任意两个顶点u,v,如果存在边u->v&#xff0c;那么在…

VMware ESXi 8.0U2c macOS Unlocker OEM BIOS ConnectX-3 网卡定制版 (集成驱动版)

VMware ESXi 8.0U2c macOS Unlocker & OEM BIOS ConnectX-3 网卡定制版 (集成驱动版) 发布 ESXi 8.0U2 集成驱动版&#xff0c;在个人电脑上运行企业级工作负载 请访问原文链接&#xff1a;https://sysin.org/blog/vmware-esxi-8-u2-sysin/&#xff0c;查看最新版。原创作…