个人主页: 起名字真南的CSDN博客
个人专栏:
- 【数据结构初阶】 📘 基础数据结构
- 【C语言】 💻 C语言编程技巧
- 【C++】 🚀 进阶C++
- 【OJ题解】 📝 题解精讲
目录
- 📌 引言
- 📌 1. 为什么 `list` 容器需要 `list_iterator`
- 📌 2. `list_iterator` 的设计与实现
- 📌 3. `list_iterator`、`list` 和 `list_node` 的关系
- 📌 4. 使用 `list_iterator` 遍历链表
- 📌 5. const 迭代器的实现
- 📌 6. 迭代器失效问题
- 📌 总结
📌 引言
📌 1. 为什么 list 容器需要 list_iterator
📌 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*。
这样一来,我们可以直接使用 *it 和 it-> 来访问节点的数据,例如:
*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_iterator、list 和 list_node 的关系
✨ 3.1 list_iterator 与 list_node
list_iterator依赖list_node:list_iterator通过_node指向list_node,实现对链表节点的访问和操作。++和--操作通过_node->_next和_node->_prev实现节点的前进和后退。- 数据访问依赖
list_node:*和->操作符的重载直接访问_node->_data,即list_node中的数据域,使得迭代器可以返回节点中的数据。 - 双向链接关系:
list_node的_prev和_next指针实现了双向链接,使得list_iterator可以方便地前后移动,而不需要依赖连续的内存地址。
✨ 3.2 list_iterator 与 list
list通过迭代器提供访问接口:list的begin()和end()返回list_iterator,分别指向链表的第一个节点和尾后节点。外部代码可以使用迭代器在list容器上进行遍历和访问。- 迭代器操作封装链表结构:
list的insert、erase等操作在返回迭代器时,允许用户在链表上插入和删除节点,保持接口一致性。 - 迭代器失效问题:在
list的erase等操作中,若迭代器失效,需要返回新的有效迭代器,这保证了链表操作的安全性。
📌 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 中使用了模板参数 Ref 和 Ptr,可以根据需求指定 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 中,Ref 和 Ptr 分别设置为 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 的关键点如下:
- 封装节点指针:
list_iterator通过持有list_node指针_node来访问和移动链表节点。 - 重载操作符:
*和->用于访问节点数据。++和--用于迭代器的前进和后退。==和!=用于迭代器的比较。
list_iterator与list、list_node的关系:list_iterator依赖list_node实现节点移动和数据访问。list通过list_iterator提供统一的接口,使链表可以通过迭代器进行遍历、插入和删除操作。
通过 list_iterator,自定义的 list 容器具备了与 STL 容器一致的遍历能力,使链表在不连续内存结构中也可以支持标准的迭代器操作。











