STL:迭代器(iteratoes)概念


前言


迭代器(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;
		}
		

	};

总结

以上就是文章大致内容,仍有诸多不足,往后如果有心思的话会修改不足之处。

  • 28
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值