[C++]list的迭代器模拟实现

下面是简单的实现list的迭代器功能,底层是带头双向循环的链表,主要关注的是list的迭代器问题。没有涉及内存池等复杂算法来提高效率。

一、list的概述

(一)、抽象数据类型的定义

|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| 容器:列表(list) |
| list是一个顺序容器,它可以在任何位置频繁地进行插入和擦除数据的操作,并且支持双向迭代。 list容器的行为就像双向链表那样,双向链表可以将每一个元素存储在不同和不连续的存储空间。其通过内部每个元素的前后链接关系保持其有序结构。 list和其他的容器相比,list在任意位置的插入、删除和移动显得更高效。而list不支持随机访问,如果要访问链表中某个位置的值,需要遍历到该位置,并且会因为要维持元素前后的关联而开辟额外的空间。 |
| 模板类 template<class T> 操作 一、构造函数 list(); 默认构造函数 list(int n, const T& value = T()); 用n个val进行填充初始化 template <class Iterator> list(Iterator first, Iterator last); 用迭代器区间进行初始化 list(std::initializer_list<T> ini_list); 参数初始化列表初始化 list(const list<T>& l); 拷贝构造函数 list<T>& operator=(const list<T>& l); 赋值构造函数 ~list(); 析构函数 二、迭代器 1、迭代器 iterator begin(); iterator end(); const_iterator begin() const; const_iterator end() const; 2、反向迭代器 reverse_iterator rbegin(); reverse_iterator rend(); const_reverse_iterator rbegin() const; const_reverse_iterator rend() const; 三、容量 size_t size()const; 返回当前容器存储的数据个数 bool empty()const; 检测当前容器是否为空 四、访问数据 T& front(); 获取list的头结点元素的引用 const T& front() const; 获取list的头结点的常引用 T& back(); 获取list的尾结点元素的引用 const T& back const; 获取list的尾节点的常引用 五、修改操作 void push_back(const T& x); 尾插 void pop_back(); 尾删 void push_fornt(const T& x); 头插 void pop_fornt(); 头插 iterator insert(iterator pos,const T& x); 指定位置插入 iterator erase(iterator pos); 删除指定位置 void swap(list<T>& v); 交换两个链表的内容 |

(二)、成员变量

cpp 复制代码
//链表的节点
template<class T>
struct ListNode
{
	T _val;
	ListNode* _pPrev;
	ListNode* _pNext;
	//生成一个链表结点并用x进行初始化
	ListNode(const T& x = T())
	{
		_val = x;
		_pPrev = this;
		_pNext = this;//该结点一开始前后指针都指向自身
	}
};


template<class T>
class list
{
public:
	typedef ListNode Node;
private:
	Node* _pHead;//指向哨兵位
};

二、具体实现

由于带头双向循环链表增删改操作比较简单,代码不进行具体讲解,只重点讲一下list的迭代器的具体实现。

(一)、list的迭代器

list不像vector一样,vector存储的数据在内存中是连续的,可以使用数组的指针加减就可以完成vector的遍历,而list的数据在逻辑结构 上是有序的,但是在其内存的存储上一般都是不连续 的,无法使用指针的加减来对链表进行遍历,不能够使用原生数组的指针来作为list的迭代器 ,这个时候我们就需要使用类来帮助我们实现list的迭代器的功能,让其行为符合我们迭代器的需求。

1、正向迭代器

在正向迭代器中,对迭代器进行++的操作,就是让迭代器指向当前结点的下一个结点,对迭代器进行--的操作,就是让迭代器指向当前结点的上一个结点,对迭代器进行解引用,得到该结点存储的数据。 要满足这样的行为,需要自定义类型来实现

具体代码如下:

cpp 复制代码
//声明定义正向迭代器
//Ref和Ptr用于辨别是普通迭代器还是const迭代器
template<class T,class Ref,class Ptr>
struct ListIterator
{
public:
	typedef ListIterator<T,Ref,Ptr> Self;
	typedef ListNode<T> Node;
	Node* _pNode;//迭代器指向当前结点的地址
	//默认构造
	ListIterator(Node* pNode = nullptr) :_pNode(pNode){; }

	//重载
	//前置++
	Self& operator++()
	{
		_pNode = _pNode->_pNext;//迭代器指向当前结点的下一个结点
		return *this;
	}
	//后置++
	Self operator++(int)
	{
		Self temp(_pNode);
		_pNode = _pNode->_pNext;//迭代器指向当前结点的下一个结点
		return temp;
	}

	//前置--
	Self& operator--()
	{
		_pNode = _pNode->_pPrev;
		return *this;
	}
	//后置--
	Self operator--(int)
	{
		Self temp(_pNode);
		_pNode = _pNode->_pNext;
		return temp;
	}

	//重载解引用
	Ref operator*()
	{
		return _pNode->_val;
	}
	//重载->操作符
	Ptr operator->()
	{
		return &(_pNode->_val);
	}

	//迭代器的比较方法(用来判断迭代停止)
	bool operator==(Self it) 
	{
		return _pNode == it._pNode;
	}
	bool operator!=(Self it)
	{
		return _pNode != it._pNode;
	}
};

2、反向迭代器

在反向迭代器中,对迭代器进行++的操作,就是让迭代器指向当前结点上一个结点,对迭代器进行--的操作,就是让迭代器指向当前结点下一个结点,对迭代器进行解引用,得到该结点存储的数据。

具体代码如下:

cpp 复制代码
//适配反向迭代器
template<class T, class Ref, class Ptr>
struct ListReverseIterator
{
public:
	typedef ListReverseIterator<T, Ref, Ptr> Self;
	typedef ListNode<T> Node;
	typedef ListNode<T>* PNode;
	PNode _pNode;

	ListReverseIterator(PNode pNode = nullptr) :_pNode(pNode){; }

	//重载++
	Self& operator++()
	{
		_pNode = _pNode->_pPrev;
		return *this;
	}
	Self operator++(int)
	{
		Self tem(_pNode);
		_pNode = _pNode->_pPrev;
		return tem;
	}

	//重载--
	Self& operator--()
	{
		_pNode = _pNode->_pNext;
		return *this;
	}
	Self operator--(int)
	{
		Self tem(_pNode);
		_pNode = _pNode->_pNext;
		return tem;
	}

	//重载解引用
	Ref operator*() 
	{
		return _pNode->value;
	}
	//重载->
	Ptr operator->() 
	{
		return &(_pNode->value);
	}

	bool operator==(const Self& it) 
	{
		return _pNode == it._pNode;
	}
	bool operator!=(const Self& it) 
	{
		return _pNode != it._pNode;
	}
};

注意:正向迭代器和反向迭代器之间由于类型不同,是不能直接进行比较的。

在定义迭代器模板类时,指明其引用类型和指针类型,可以避免写重复的代码段。

(二)、list的具体实现

cpp 复制代码
//带头双向循环list容器
template<class T>
class list
{
public:
	typedef ListNode<T> Node;
	typedef ListNode<T>* PNode;

	//迭代器
	typedef ListIterator<T, T&, T*> iterator;
	typedef ListIterator<T, const T&, const T*> const_iterator;
	//反向迭代器
	typedef ListReverseIterator<T, T&, T*> reverse_iterator;
	typedef ListReverseIterator<T, const T&, const T*> const_reverse_iterator;

	//初始化
	list()
	{
		_pHead = new Node;
		_pHead->_pNext = _pHead;
		_pHead->_pPrev = _pHead;
	}

	list(int n, const T& value = T()) :list()
	{
		for (int i = 0; i < n; i++)
		{
			push_back(value);
		}
	}
	template <class Iterator>
	list(Iterator first, Iterator last) :list()
	{
		while (first != last)
		{
			push_back(*first);
			first++;
		}
	}
	//参数初始化列表初始化
	list(std::initializer_list<T> ini_list) :list()
	{
		for (auto& e : ini_list)
		{
			push_back(e);
		}
	}
	//拷贝构造
	list(const list<T>& l) :list()
	{
		
		for (auto& e : l)
		{
			push_back(e);
		}
	}
	//赋值构造
	list<T>& operator=(const list<T>& l)
	{
		list<T> tem(l);
		swap(tem);
		return *this;
	}

	//析构函数
	~list()
	{
		PNode pcur = _pHead->_pNext;
		PNode ptem;//记录销毁节点的下一个节点
		//std::cout << "销毁:";
		while (pcur != _pHead)
		{
			ptem = pcur->_pNext;
			//cout << (pcur->value) << "->";
			delete pcur;
			pcur = ptem;
		}
		//std::cout << "nullptr" << std::endl;
		_pHead = nullptr;
	}

	//迭代器
	iterator begin()
	{
		return iterator(_pHead->_pNext);
	}
	iterator end()
	{
		return iterator(_pHead);
	}
	const_iterator begin() const
	{
		return const_iterator(_pHead->_pNext);
	}
	const_iterator end() const
	{
		return const_iterator(_pHead);
	}

	//反向迭代器
	reverse_iterator rbegin()
	{
		return reverse_iterator(_pHead->_pPrev);
	}
	reverse_iterator rend()
	{
		return reverse_iterator(_pHead);
	}
	const_reverse_iterator rbegin() const
	{
		return const_reverse_iterator(_pHead->_pPrev);
	}
	const_reverse_iterator rend() const
	{
		return const_reverse_iterator(_pHead);
	}


	// List Capacity
	size_t size()const
	{
		PNode pcur = _pHead->_pNext;
		size_t n = 0;
		while (pcur != _pHead)
		{
			n++;
			pcur = pcur->_pNext;
		}
		return n;
	}
	bool empty()const
	{
		return _pHead == _pHead->_pNext;
	}

	// List Access
	T& front()
	{
		if (!empty())
			return _pHead->_pNext->value;
		else
			return _pHead->value;
	}
	const T& front()const
	{
		if (!empty())
			return _pHead->_pNext->value;
		else
			return _pHead->value;
	}
	T& back()
	{
		if (!empty())
			return _pHead->_pPrev->value;
		else
			return _pHead->value;
	}
	const T& back()const
	{
		if (!empty())
			return _pHead->_pPrev->value;
	}
	//Modify
	//尾插
	void push_back(const T& x)
	{
		//找到尾结点
		PNode pTail = _pHead->_pPrev;
		PNode newNode = new Node(x);
		newNode->_pNext = _pHead;
		newNode->_pPrev = pTail;
		pTail->_pNext = newNode;
		_pHead->_pPrev = newNode;
	}

	//尾删
	void pop_back()
	{
		if (empty())
			return;

		PNode tail = _pHead->_pPrev;
		tail->_pPrev->_pNext = tail->_pNext;
		tail->_pNext->_pPrev = tail->_pPrev;
		delete tail;
	}
	//头插
	void push_fornt(const T& x)
	{
		PNode newNode = new Node(x);
		newNode->_pPrev = _pHead;
		newNode->_pNext = _pHead->_pNext;
		_pHead->_pNext->_pPrev = newNode;
		_pHead->_pNext = newNode;
	}
	//头删
	void pop_fornt()
	{
		if (empty())
			return;


		PNode pDel = _pHead->_pNext;
		pDel->_pPrev->_pNext = pDel->_pNext;
		pDel->_pNext->_pPrev = pDel->_pPrev;
		delete pDel;
	}
	//指定位置插入
	//不存在迭代器失效问题
	iterator insert(iterator pos, const T& x)
	{
		PNode newNode = new Node(x);
		newNode->_pNext = pos._pNode;
		newNode->_pPrev = pos._pNode->_pPrev;
		pos._pNode->_pPrev->_pNext = newNode;
		pos._pNode->_pPrev = newNode;
		return iterator(newNode);
	}
	//指定位置删除
	//返回删除位置的下一个节点的迭代器,因为会存在迭代器失效问题
	iterator erase(iterator pos)
	{
		iterator itNext(pos._pNode->_pNext);
		//连接前后节点
		pos._pNode->_pPrev->_pNext = pos._pNode->_pNext;
		pos._pNode->_pNext->_pPrev = pos._pNode->_pPrev;
		delete pos._pNode;
		return itNext;
	}
	//交换两个链表内容的操作
	void swap(list<T>& v1)
	{
		std::swap(_pHead, v1._pHead);
	}
private:
	//头节点指针
	PNode _pHead;
	//size_t _length;
};

注意:在指定位置插入一个结点不会存在迭代器失效问题,而删除指定位置的结点时,就会存在迭代器失效的问题,此时迭代器指向不存在的结点。因此,在删除指定位置的结点时,应将其下一个结点的迭代器作为返回值返回,更新迭代器的值,防止迭代器失效问题。

相关推荐
林开落L11 分钟前
前缀和算法习题篇(上)
c++·算法·leetcode
远望清一色12 分钟前
基于MATLAB边缘检测博文
开发语言·算法·matlab
何曾参静谧20 分钟前
「Py」Python基础篇 之 Python都可以做哪些自动化?
开发语言·python·自动化
Prejudices24 分钟前
C++如何调用Python脚本
开发语言·c++·python
单音GG26 分钟前
推荐一个基于协程的C++(lua)游戏服务器
服务器·c++·游戏·lua
我狠狠地刷刷刷刷刷36 分钟前
中文分词模拟器
开发语言·python·算法
wyh要好好学习40 分钟前
C# WPF 记录DataGrid的表头顺序,下次打开界面时应用到表格中
开发语言·c#·wpf
AitTech40 分钟前
C#实现:电脑系统信息的全面获取与监控
开发语言·c#
qing_04060343 分钟前
C++——多态
开发语言·c++·多态
孙同学_43 分钟前
【C++】—掌握STL vector 类:“Vector简介:动态数组的高效应用”
开发语言·c++