文章目录
前言
迭代器(Iterator)是一种用于访问容器(如数组、向量、列表等)中元素的通用机制。迭代器提供了一种方法,能够按顺序遍历容器中的元素,同时保持容器与迭代器之间的解耦,这意味着迭代器的设计不依赖于容器的内部结构。
本文概念定义主要参考《STL源码剖析》,以下将会梳理迭代器相关的概念,以及用list中的迭代器的模拟实现来大致阐述其作用。
迭代器是一种抽象的设计概念,现实编程语言中并没有直接对应于这个概念的实物
一、迭代器设计思维——STL关键所在
1.1 迭代器存在原因解释
不论是泛型思维或者STL的运用,迭代器(iterators)都扮演着重要的角色。STL的中心思想在于:将数据容器(containers)和算法(algorithms)分开,彼此独立设计,最后再以一贴胶着剂将它们撮合在一起。容器和算法的泛型化,从技术角度来看并不困难,c++的class template和function template可分别达成目标。可是如何设计两者之间的良好胶着剂,才是大难题。
以下是容器,算法,迭代器(iterators,扮演粘胶角色)的合作展示。以算法find() 为例,它接受两个迭代器和一个“搜寻目标”
//摘自 SGI <stl_algo.h>
template<class InputIterator,class T>
InputIterator find(InputIterator first,
InputIterator last,
const T& value){
while(first!=last && *first!=value)
++first;
return first;
}
1.2 迭代器是一种smart pointer
迭代器是一种行为类似指针的对象,而指针的各种行为中最常见最重要的是内容提领(dereference)和成员访问(member access),因此,迭代器最重要的编程工作就是对operator* 和operator-> 进行重载(overloading)工作。
举例:当我们定义一个链表类时里面定义了三个类型,我们如果想访问数据_data时而不是它的_next和__prev就不能直接像向量那样直接解引用操作
struct List_Node {//存储节点及构造函数的定义
List_Node(const T& data=T())
:_data(data)
,_next(nullptr)
,_prev(nullptr)
{
}
T _data;
List_Node<T>* _next;
List_Node<T>* _prev;
};
此时我们想具体访问某个数据,就需要再定义一个类来完成工作(这就是iterator的作用)
关于这一点,c++标准库有一个auto_ptr可供我们参考(这是一个用来包装原生指针(native pointer)的对象,声名狼藉的内存泄露(memory leak)问题可藉此获得解决)
以下是对auto_ptr的仿真简化版,可具体说明auto_ptr的行为:
template<class T>
class auto_ptr{
public:
explicit auto_ptr(T* =0):pointer(p) {}
template<class U>
auto_ptr(auto_ptr<U>& rhs):pointor(rhs.release()){}
~auto_ptr(){delete pointor;}
private:
T* pointer;
}
二.list中的迭代器模拟实现以及作用
2.1迭代器的类型
1.输入迭代器(Input Iterators):
• 最基本的迭代器类型,只能向前移动(即只能递增),并且只能读取它所指向的元素的值,但不能修改它。
• 输入迭代器通常用于只读访问序列中的元素,如从文件或输入流中读取数据。
2.输出迭代器(Output Iterators):
• 与输入迭代器相反,输出迭代器只能写入它所指向的元素的值,但不能读取它。
• 输出迭代器通常用于向文件、输出流或容器中添加数据。
• 它们也只能向前移动。
3.前向迭代器(Forward Iterators):
• 提供了输入迭代器的所有功能,并且允许多次通过相同的迭代器访问同一个元素(即可以保存迭代器的副本并在之后使用)。
• 仍然只能向前移动,但比输入迭代器更强大,因为它们支持多次传递算法。
4.双向迭代器(Bidirectional Iterators):
• 提供了前向迭代器的所有功能,并且支持向后移动(即可以递减)。
• 这使得双向迭代器能够遍历序列中的元素,并在需要时回溯到之前的元素。
5.随机访问迭代器(Random Access Iterators):
• 提供了双向迭代器的所有功能,并且支持随机访问序列中的元素。
• 这意味着可以使用迭代器进行算术运算(如加法、减法、比较等),以直接跳转到序列中的任何位置。
• 随机访问迭代器是最强大的迭代器类型,提供了对容器元素的最灵活访问方式。
总结:迭代器的类型决定了当前迭代器能实现的操作(例如对于一个随机迭代器我们可以对其进行++/-- /+/-等操作,而对于双向指针我们就只能进行++/-- 操作)
2.2双向迭代器与list
2.2.1list的数据结构
以下是list的数据结构定义
template<class T>
struct List_Node {//存储节点及构造函数的定义
List_Node(const T& data=T())
:_data(data)
,_next(nullptr)
,_prev(nullptr)
{
}
T _data;
List_Node<T>* _next;
List_Node<T>* _prev;
};
template<class T>
class list {//带哨兵位的链表的定义
typedef List_Node<T> Node;
public:
list()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
private:
Node* _head;//这里作者本人在调试的时候不是用指针Node*而是类型Node,编译器一直报错List_node没有重载
//函数operator->,还以为是前面List_Node里面的代码问题,调试一个多小时才找到在定义时候少些个*号导致类型错误。
size_t _size;
};
2.2.2迭代器的模拟实现
这里主要用一个模板类来进行迭代器的实现,这段代码定义了一个模板类 list_iterator,它是用于遍历自定义链表(链表的节点类型为 list_node)的迭代器。它提供了一种方法来访问容器中的元素,而无需暴露容器的内部表示。
template<class T,class Ptr,class Ref>//当我们将它作为类型在其它类里面声明的时候,可以进行如下声明来区分const和非const
//typedef list_iterator<T, T&, T*> iterator;
//typedef list_iterator<T, const T&, const T*> const_iterator;
//实例化的时候就会匹配实例化
struct list_iterator
{
typedef list_node<T> Node;
typedef list_iterator<T,Ptr,Ref> Self;
Node* _node;
//构造函数,它接受一个指向链表节点的指针作为参数,并将其赋值给成员变量 _node。
//这允许在创建迭代器时指定它应该指向链表的哪个节点
//例如:当我们将它包含在list中,那么当我们进行声明的时候,list::list_iterator (变量名)就可以作为左值接受一个函数的同类型返回值。
list_iterator(Node* node=nullptr)
:_node(node)
{}
T& operator*()
{
return _node->_data;
}
T* operator->()
{
return &_node->_data;
}
Self& operator++()
{
_node = _node->_next;
return *this;
}
Self& operator--()
{
_node = _node->_prev;
return *this;
}
};
总结
以上就是文章大致内容,仍有诸多不足,往后如果有心思的话会修改不足之处。