【C++】深入理解自定义 list 容器中的 list_iterator:迭代器实现详解

个人主页: 起名字真南的CSDN博客

个人专栏:

  • 【数据结构初阶】 📘 基础数据结构
  • 【C语言】 💻 C语言编程技巧
  • 【C++】 🚀 进阶C++
  • 【OJ题解】 📝 题解精讲

目录

  • 📌 引言
  • 📌 1. 为什么 `list` 容器需要 `list_iterator`
  • 📌 2. `list_iterator` 的设计与实现
    • ✨ 2.1 `list_iterator` 的基本结构
    • ✨ 2.2 重载 `*` 和 `->` 操作符
    • ✨ 2.3 重载 `++` 和 `--` 操作符
      • 🚀 前置 `++` 和后置 `++`
      • 🚀 前置 `--` 和后置 `--`
    • ✨ 2.4 重载比较运算符 `==` 和 `!=`
  • 📌 3. `list_iterator`、`list` 和 `list_node` 的关系
    • ✨ 3.1 `list_iterator` 与 `list_node`
    • ✨ 3.2 `list_iterator` 与 `list`
  • 📌 4. 使用 `list_iterator` 遍历链表
  • 📌 5. const 迭代器的实现
  • 📌 6. 迭代器失效问题
  • 📌 总结

📌 引言

在上一篇文章中,我们从零实现了一个 list 容器,包括节点结构、迭代器设计、增删查操作等。然而,对于一个成熟的容器来说,迭代器是不可或缺的部分,因为它提供了遍历和访问容器元素的标准接口。本篇文章将补充说明 list_iterator 的设计和实现,帮助大家深入理解迭代器的原理以及在 list 容器中的重要作用。


📌 1. 为什么 list 容器需要 list_iterator

list 是一种双向链表,节点之间的内存地址并不连续。为了支持容器的标准遍历接口,必须通过迭代器封装节点间的前后关系。list_iterator 实现了 ++--*-> 等操作符,使得我们可以在链表上使用 STL 的标准迭代器操作,并方便地对节点数据进行访问和修改。


📌 2. list_iterator 的设计与实现

✨ 2.1 list_iterator 的基本结构

list_iterator 是一个模板类,内部封装了指向链表节点的指针 _node。通过 _node,迭代器可以在节点间移动,并访问节点的数据。

template<class T, class Ref, class Ptr>
struct list_iterator {typedef list_node<T> Node;                 // 节点类型typedef list_iterator<T, Ref, Ptr> Self;   // 迭代器类型Node* _node;                               // 当前节点的指针// 构造函数,初始化为指定节点list_iterator(Node* node = nullptr) : _node(node) {}
};
  • 模板参数

    • T:节点中数据的类型。
    • Ref* 操作符的返回类型,通常为 T&const T&
    • Ptr-> 操作符的返回类型,通常为 T*const T*
  • 成员变量 _node:指向当前节点的指针,用于定位链表中的一个节点。


✨ 2.2 重载 *-> 操作符

为了让迭代器可以像指针一样访问节点数据,我们重载了 *-> 操作符。这两个操作符分别返回节点的数据值和数据地址。

Ref operator*() {return _node->_data;  // 返回节点的数据引用
}Ptr operator->() {return &_node->_data; // 返回节点数据的地址
}
  • operator*:返回当前节点的数据引用,类型为 Ref,通常为 T&const T&
  • operator->:返回节点数据的地址,类型为 Ptr,通常为 T*const T*

这样一来,我们可以直接使用 *itit-> 来访问节点的数据,例如:

*it += 10;          // 修改当前节点的数据
cout << it->value;  // 访问节点数据成员

✨ 2.3 重载 ++-- 操作符

在链表中,前进和后退一个节点的操作不是简单的指针加减,而是通过 _next_prev 指针。因此,我们重载 ++-- 运算符,使迭代器能够在节点间移动。

🚀 前置 ++ 和后置 ++

Self& operator++() {_node = _node->_next;return *this;
}Self operator++(int) {Self tmp(*this);        // 创建当前迭代器的临时副本_node = _node->_next;   // 将 _node 指向下一个节点return tmp;             // 返回旧的迭代器
}
  • 前置 ++:将 _node 指针移动到下一个节点,返回修改后的迭代器自身。
  • 后置 ++:先保存当前迭代器的副本,再将 _node 指向下一个节点,最后返回未修改的副本。

🚀 前置 -- 和后置 --

Self& operator--() {_node = _node->_prev;return *this;
}Self operator--(int) {Self tmp(*this);         // 创建当前迭代器的临时副本_node = _node->_prev;    // 将 _node 指向前一个节点return tmp;              // 返回旧的迭代器
}
  • 前置 --:将 _node 指向前一个节点,返回修改后的迭代器自身。
  • 后置 --:先保存当前迭代器的副本,再将 _node 移动到前一个节点,最后返回旧的副本。

通过这两个运算符的重载,我们可以在链表上实现正向和反向遍历,符合 STL 迭代器的标准行为。


✨ 2.4 重载比较运算符 ==!=

为了判断两个迭代器是否指向相同的节点,我们重载了 ==!= 运算符。当两个迭代器的 _node 指针相等时,它们表示相同的位置。

bool operator==(const Self& other) const {return _node == other._node;
}bool operator!=(const Self& other) const {return _node != other._node;
}
  • operator==:比较两个迭代器的 _node 指针是否相等。
  • operator!=:判断两个迭代器是否不相等,通常用于循环结束条件。

📌 3. list_iteratorlistlist_node 的关系

✨ 3.1 list_iteratorlist_node

  • list_iterator 依赖 list_nodelist_iterator 通过 _node 指向 list_node,实现对链表节点的访问和操作。++-- 操作通过 _node->_next_node->_prev 实现节点的前进和后退。
  • 数据访问依赖 list_node*-> 操作符的重载直接访问 _node->_data,即 list_node 中的数据域,使得迭代器可以返回节点中的数据。
  • 双向链接关系list_node_prev_next 指针实现了双向链接,使得 list_iterator 可以方便地前后移动,而不需要依赖连续的内存地址。

✨ 3.2 list_iteratorlist

  • list 通过迭代器提供访问接口listbegin()end() 返回 list_iterator,分别指向链表的第一个节点和尾后节点。外部代码可以使用迭代器在 list 容器上进行遍历和访问。
  • 迭代器操作封装链表结构listinserterase 等操作在返回迭代器时,允许用户在链表上插入和删除节点,保持接口一致性。
  • 迭代器失效问题:在 listerase 等操作中,若迭代器失效,需要返回新的有效迭代器,这保证了链表操作的安全性。

📌 4. 使用 list_iterator 遍历链表

借助 list_iterator,我们可以像遍历数组那样遍历链表。例如,下面的代码展示了如何使用迭代器遍历自定义 list 容器。

list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);list<int>::iterator it = lt.begin();
while (it != lt.end()) {cout << *it << " ";  // 输出当前节点的数据++it;                 // 前进到下一个节点
}

在上述代码中,begin() 返回链表首节点的迭代器,end() 返回链表尾后位置的迭代器。通过 ++it 操作符,我们可以依次访问链表的每一个节点。


📌 5. const 迭代器的实现

list_iterator 中使用了模板参数 RefPtr,可以根据需求指定 T&const T&,从而支持常量迭代器 const_iterator。当使用 const_iterator 时,数据不可被修改。

list<int> lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);list<int>::const_iterator it = lt.begin();
while (it != lt.end()) {cout<< *it << " ";  // 仅能读取数据,不能修改++it;
}

const_iterator 中,RefPtr 分别设置为 const T&const T*,确保 *it 不能用于修改节点的数据。


📌 6. 迭代器失效问题

在链表中删除或插入元素时,可能导致迭代器失效。例如,在 erase 删除某个节点后,指向该节点的迭代器将变为无效,继续使用会引发错误。因此,在 erase 函数中返回下一个有效迭代器,以确保遍历过程中不会访问失效的节点。

auto it = lt.begin();
while (it != lt.end()) {if (*it % 2 == 0) {it = lt.erase(it);  // 删除节点,返回下一个有效迭代器} else {++it;}
}

📌 总结

通过 list_iterator,我们实现了自定义 list 容器的标准遍历方式。总结 list_iterator 的关键点如下:

  1. 封装节点指针list_iterator 通过持有 list_node 指针 _node 来访问和移动链表节点。
  2. 重载操作符
    • *-> 用于访问节点数据。
    • ++-- 用于迭代器的前进和后退。
    • ==!= 用于迭代器的比较。
  3. list_iteratorlistlist_node 的关系
    • list_iterator 依赖 list_node 实现节点移动和数据访问。
    • list 通过 list_iterator 提供统一的接口,使链表可以通过迭代器进行遍历、插入和删除操作。

通过 list_iterator,自定义的 list 容器具备了与 STL 容器一致的遍历能力,使链表在不连续内存结构中也可以支持标准的迭代器操作。


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

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

相关文章

昆明华厦眼科医院举办中外专家眼科技术研讨会

9月13日&#xff0c;“睿智迭代&#xff0c;增效赋能”Menicon Z Night中外专家研讨会在昆明华厦眼科医院成功举办。此次会议由目立康公司与昆明华厦眼科医院携手共筑&#xff0c;标志着双方合作迈向新的高度。 昆明华厦眼科医院总经理王若镜首先发表了热情洋溢的致辞&#xff…

FreeRTOS的列表与列表项

目录 1.为什么要学列表&#xff1f; 2.什么是列表和列表项&#xff1f; 2.1 列表 2.2列表项 2.3&#xff0c;迷你列表项 3.列表与列表项的初始化 3.1 列表初始化 3.2列表项初始化 4.列表项的“增删查”&#xff08;插入、删除、遍历&#xff09; 4.1列表项的插入 4.1.1…

前端(3)——快速入门JaveScript

参考&#xff1a; 罗大富 JavaScript 教程 | 菜鸟教程 JavaScript 教程 1. JaveScript JavaScript 简称 JS JavaScript 是一种轻量级、解释型、面向对象的脚本语言。它主要被设计用于在网页上实现动态效果&#xff0c;增加用户与网页的交互性。作为一种客户端脚本语言&#…

使用阿里云快速搭建 DataLight 平台

使用阿里云快速搭建 DataLight 平台 本篇文章由用户 “闫哥大数据” 分享&#xff0c;B 站账号&#xff1a;https://space.bilibili.com/357944741?spm_id_from333.999.0.0 注意&#xff1a;因每个人操作顺序可能略有区别&#xff0c;整个部署流程如果出现出入&#xff0c;以…

H.265流媒体播放器EasyPlayer.js H.264/H.265播放器chrome无法访问更私有的地址是什么原因

EasyPlayer.js H5播放器&#xff0c;是一款能够同时支持HTTP、HTTP-FLV、HLS&#xff08;m3u8&#xff09;、WS、WEBRTC、FMP4视频直播与视频点播等多种协议&#xff0c;支持H.264、H.265、AAC、G711A、MP3等多种音视频编码格式&#xff0c;支持MSE、WASM、WebCodec等多种解码方…

QT_CONFIG宏使用

时常在Qt代码中看到QT_CONFIG宏&#xff0c;之前以为和#define、DEFINES 差不多&#xff0c;看了定义才发现不是那么回事&#xff0c;定义如下&#xff1a; 看注释就知道了QT_CONFIG宏&#xff0c;其实是&#xff1a;实现了一个在编译时期安全检查&#xff0c;检查指定的Qt特性…

centos7安装Chrome使用selenium-wire

背景&#xff1a;在centos7中运行selenium-wire爬虫&#xff0c;系统自带的Firefox浏览器不兼容&#xff0c;运行报错no attribute ‘set_preference’&#xff0c;应该是selenium-wire和Firefox的驱动不兼容 查了半天不知道怎么解决&#xff0c;就想在centos7上安装Chrome来跑…

医院信息化与智能化系统(21)

医院信息化与智能化系统(21) 这里只描述对应过程&#xff0c;和可能遇到的问题及解决办法以及对应的参考链接&#xff0c;并不会直接每一步详细配置 如果你想通过文字描述或代码画流程图&#xff0c;可以试试PlantUML&#xff0c;告诉GPT你的文件结构&#xff0c;让他给你对应…

《FreeRTOS任务控制块篇》

Task control block, 即任务控制块。任务控制块&#xff08;TCB&#xff09;是一个结构体&#xff0c;它会分配给每个任务&#xff0c;其中存储着任务的状态信息&#xff0c;包括指向任务上下文&#xff08;任务的运行时环境&#xff0c;包括寄存器值&#xff09;的指针。任务控…

Queuing 表(buffer表)的优化实践 | OceanBase 性能优化实践

案例问题描述 该案例来自一个金融行业客户的问题&#xff1a;他们发现某个应用对一个数据量相对较小的表&#xff08;仅包含数千条记录&#xff09;访问时&#xff0c;频繁遇到性能下降的情况。为解决此问题&#xff0c;客户向我们求助进行分析。我们发现这张表有频繁的批量插…

ssh登陆服务器后支持Tab键命令补全

在服务器上新建了用户后&#xff0c;通过ssh登录到服务器后发现不能使用Tab键来进行命令补全 截图如下&#xff1a; 以为没有配置.bashrc 此时输入 source 发现无此命令 细心的可以发现 -sh 于是输入命令echo $SHELL 确认此时的shell为sh&#xff0c; 只要输入命令bash即可切…

[白月黑羽]关于仿写类postman功能软件题目的解答

原题&#xff1a; 答&#xff1a; python文件如下 from PySide6.QtWidgets import QApplication, QMessageBox,QTableWidgetItem,QHeaderView,QWidget,QTableWidget from PySide6.QtCore import QEvent,QObject from PySide6.QtUiTools import QUiLoader import time import …

Postman接口测试(断言、关联、参数化、输出测试报告)

基本界面展示 Get、Post请求 Postman断言 使用postman来判断预期结果与实际结果是否一致 响应状态码断言 响应包含字符串 断言判断字符串的格式 关联 用于解决http请求之间存在依赖关系 依赖&#xff1a;一个http请求的响应结果中的数据&#xff0c;被另一个请求使用 登…

【卡尔曼滤波】数据融合Fusion的应用 C语言、Python实现(Kalman Filter)

【卡尔曼滤波】数据融合Fusion的应用 C语言、Python实现&#xff08;Kalman Filter&#xff09; 更新以gitee为准&#xff1a; gitee地址 文章目录 卡尔曼滤波数据融合Python实现C语言实现多个数据如何融合附录&#xff1a;压缩字符串、大小端格式转换压缩字符串浮点数压缩Pac…

网络原理-网络层和数据链路层

一、网络层 1、IP协议完成的工作 地址管理&#xff1a;使用一套地址体系来描述所没备的位置 路由选择&#xff1a;一个数据包如何从网络的某个地址传到另一个地址 2、IP报头 4 位版本号&#xff1a;取值为4或6 (IPv4/IPv6) 4 位首部长度&#xff1a;IP报头&#xff0c;单位…

【Three.js基础学习】22.New project structure

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 前言 这里将使用全新的项目结构&#xff0c;将不同工具分层&#xff0c;区分开使用。 一、结构目录 二、对应文件 1.script.js 获取画布&#xff0c;引入样式和功能。 /* 课…

AI风向标|算力与通信的完美融合,SRM6690解锁端侧AI的智能密码

当前&#xff0c;5G技术已经成为推动数字经济和实体经济深度融合的关键驱动力&#xff0c;进入5G发展的下半场&#xff0c;5G与AI的融合正推动诸多行业的数字化转型和创新发展&#xff0c;终端侧AI和端云混合式AI将广泛应用于各类消费终端和各行各业。 在推动5G和AI与各行业场…

【WPF】Prism学习(二)

Prism Commands 1.命令&#xff08;Commanding&#xff09; 1.1. ViewModel的作用&#xff1a; ViewModel不仅提供在视图中显示或编辑的数据&#xff0c;还可能定义一个或多个用户可以执行的动作或操作。这些用户可以通过用户界面&#xff08;UI&#xff09;执行的动作或操作…

智慧建造-运用Trimble技术将梦幻水族馆变为现实【上海沪敖3D】

项目概述 西雅图水族馆耗资1.6亿美元对海洋馆进行扩建。该项目包括建造三个大型栖息地&#xff0c;每个建筑物几乎都没有直边&#xff0c;其中一个主栖息地由520立方米混凝土和355吨钢筋组成。特纳建筑公司的混凝土团队通过强大的贸易合作伙伴和创新的数字制造技术&#xff0c;…

kubesphere环境-本地Harbor仓库+k8s集群(单master 多master)+Prometheus监控平台部署

前言&#xff1a;半月前在公司生产环境上离线部署了k8s集群Victoria Metrics(二开版)自研版夜莺 监控平台的搭建&#xff0c;下面我租用3台华为云服务器演示部署kubesphere环境-本地Harbor仓库k8s集群&#xff08;单master节点 & 单master节点&#xff09;Prometheus监控部…