迭代器概念以及list模拟实现

迭代器设计思维

在学习STL的时候,迭代器扮演着重要的角色,STL的中心思想在于:将容器和算法独立分开,之后再设计一种胶着剂将他们撮合在一起。而迭代器起到了重要的作用。

迭代器是一种智能指针

迭代器是一种行为类似指针的对象,没错,迭代器是一个对象,而指针最基本的功能有“解引用”,“成员访问”,所以迭代器最重要的就是对这两个操作符进行运算符重载。

这里我们以STL的容器list为例子演示一下:

list是一个双向带头链表,所以首先要明白一点:list不支持通过下标随机访问表中元素,在模拟实现list的时候,我们首先创建一个list_node的类,类成员变量即为每个节点所含的信息。

	template<class T>
	struct list_node
	{
		T _data;
		list_node<T>* _next;
		list_node<T>* _prev;
		
		list_node(const T& x=T())
			:_data(x),
			_next(nullptr),
			_prev(nullptr)
		{}
	};

其次创建一个list的类,但是我们会发现list的许多操作都涉及到了迭代器。list这个类只有唯一的一个类成员,就是list的头节点head。

	template<class T>
	class list
	{
		typedef list_node<T> Node;
	public:
		typedef __list_iterator<T,T&,T*> iterator;
		typedef __list_iterator<T,const T&, const T*> const_iterator;

		typedef myiterator::__reverse_iterator<iterator, T&, T*> reverse_iterator;
		typedef myiterator::__reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;

		iterator begin()
		{
			return iterator(_head->_next);
		}
		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}
		iterator end()
		{
			return iterator(_head);
		}
		const_iterator end() const
		{
			return const_iterator(_head);
		}
		iterator rbegin()
		{
			return reverse_iterator(end());
		}
		list()
		{
			empty_init();
		}

		void empty_init()
		{
			//创建并初始化哨兵位的头节点
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}

		void push_back(const T& x)
		{
			Node* tail = _head->_prev;
			Node* newnode = new Node(x);
			tail->_next = newnode;
			newnode->_prev = tail;
			newnode->_next = _head;
			_head->_prev = newnode;
		}
		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;

			Node* newnode = new Node(x);
			cur->_next = newnode;
			newnode->_next = cur;
			newnode->_prev = prev;
			cur->_prev = newnode;
			return iterator(newnode);
		}
		iterator erase(iterator pos)
		{
			assert(pos != end());

			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* next = cur->_next;
			prev->_next = next;
			next->_prev = prev;
			delete cur;
			return iterator(next);
		}
		list<T>& operator = (list<T> lt)
		{
			swap(lt);
			return *this;
		}
		void pop_back()
		{
			erase(--end());
		}
		void pop_head()
		{
			erase(begin());
		}
		template<class InputIterator>
		list(InputIterator first,InputIterator last)
		{
			empty_init();
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}
		void swap(list<T>& x)
		//void swap(list x) 在类里面也可以不写<T>
		{
			std::swap(_head, x._head);
		}
		//拷贝构造
		list(const list<T>& x)
		{
			empty_init();
			list<T> tmp(x.begin(), x.end());
			swap(tmp);
		}
		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}

	private:
		Node* _head;
	};

基本的接口这里就不做详细解释了,增删查改的结构之前的博客中也有。

list的构造函数

		template<class InputIterator>
		list(InputIterator first,InputIterator last)
		{
			empty_init();
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

在官方的原文档中有很多list的构造函数的重载,但是这里只模拟实现了一种迭代器分别指向头尾的方法,这里就要提到一个迭代器的知识点了:前闭后开

在list中,存储的节点并不像vector一样是连续存放的,所以判断的时候要使用!=,其次就是为什么要设计为前闭后开(first指向第一个元素,last指向最后一个元素的下一个地方),这里就要说一下一个查找函数find,当没有找到的时候就会返回指定范围的最后一个元素的下一个位置。

list的拷贝构造以及swap函数的应用

		void swap(list<T>& x)
		//void swap(list x) 在类里面也可以不写<T>
		{
			std::swap(_head, x._head);
		}
		//拷贝构造
		list(const list<T>& x)
		{
			empty_init();
			list<T> tmp(x.begin(), x.end());
			swap(tmp);
		}
		list<T>& operator = (list<T> lt)
		{
			swap(lt);
			return *this;
		}

这里的拷贝构造先将当前对象初始化,初始化之后再创建一个tmp对象,并对其进行初始化,之后进行交换,应为list类只有一个成员变量就是_head,所以也只需要将这一个元素进行交换即可。还有一个可以使用swap就是上面代码中的=运算符重载,函数传入的参数是一个拷贝,所以可以直接进行交换后返回。

list中的迭代器

 首先我们知道了迭代器是一个类,所以首先写一下这个类的代码

	//迭代器
	template<class T,class Ref,class Ptr>
	struct __list_iterator
	{
		typedef list_node<T> Node;
		Node* _node; 
		typedef __list_iterator iterator;
		__list_iterator(Node* node)
			:_node(node)
		{}
		//运算符重载
		bool operator !=(const iterator& it) const
		{
			return _node != it._node;
		}
		bool operator ==(const iterator& it) const
		{
			return _node == it._node;
		}
		Ref operator *()
		{
			return _node->_data;
		}
		//重载->
		Ptr operator->()
		{
			return &(operator*());//<=> return &(_node->_data);
		}
		//后置++
		iterator& operator ++()
		{
			_node = _node->_next;
			return *this;
		}
		//前置++
		iterator& operator++(int)
		{
			iterator tmp(*this);
			_node = _node->_next;
			return tmp;
		}
		//后置--
		iterator& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		//前置--
		iterator& operator--(int)
		{
			iterator tmp(*this);
			_node = _node->_prev;
			return tmp;
		}
	};

上面的代码我们实现了一个正向的普通迭代器,之后会介绍反向迭代器。上面的代码介绍了基本的迭代器封装。list实现的迭代器实现的功能有正常的++,--的移动,由于list不支持通过 [] 来随机访问节点元素,其他的一些封装我们上面也提到过了。但是迭代器的类的实现最终要的是和list合起来:

这里的一个细节值得我们注意:迭代器的类模板中有三个参数,在list类模板中使用的时候,因为要在设计普通迭代器的同时也要设计一个const迭代器,但是如果再去设计一个const迭代器的类明显有些冗余了,所以这里C++源码给出的答案就像上面代码中的内容:

        typedef __list_iterator<T,T&,T*> iterator;
        typedef __list_iterator<T,const T&, const T*> const_iterator;

		iterator begin()
		{
			return iterator(_head->_next);
		}
		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}
		iterator end()
		{
			return iterator(_head);
		}
		const_iterator end() const
		{
			return const_iterator(_head);
		}

begin和end分别设计了普通版本和const版本,返回的方式是调用迭代器的构造函数,传入一个指向链表的节点的指针,构造出一个迭代器对象。

在迭代器的类成员函数中也有很多细节的东西:

在重载运算符" * "的时候,返回值为引用,这就避免了返回值拷贝的问题。因为我们并不知道list的每个成员的成员类型是什么。

有一个运算符的重载有点意思,“->” 实际就是先通过对象找到成员变量的地址,之后对其进行返回

通过算法中的find函数,以及list的模拟实现,可以发现迭代器在这些功能的实现中起到了粘着剂的关键作用。本篇的iterator的讲解偏向基础,之后会有更深入的讲解,敬请期待!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Feng,

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值