【C++】STL—— list 模拟实现

📕 list 简介

vector 是一个和数组类似的容器,list 实际上就是和链表类似的容器。它对数据的操作,本质上就是对链表的增删查改,底层是双向链表。如果对双向带头循环链表不熟悉,可以尝试先看一看这篇文章:双向带头循环链表的实现
对比链表和顺序表,vector 和 list 的优劣势也类似的。list 可以在任意位置高效地增删查改,但是不可以随机访问容器里的数据。

📕 list 模拟实现

框架

框架如下,首先,list 是链表的形式,所以必须要定义一个struct 来存储链表的节点,__list_node 就是存储节点的结构体。由于是双向链表,所以每个节点有两个指针。
其次,和 string、vector 不同的是,list 将原生指针进行封装,成了迭代器(前两个是直接把指针当作迭代器)。至于为什么迭代器的模板参数有三个,后面实现的时候会有详细解释。

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

namespace simulate
{
	template<class T>
	struct __list_node
	{
		__list_node<T>* _prev;
		__list_node<T>* _next;
		T _data;

		__list_node(const T& x=T())
			:_prev(nullptr)
			,_next(nullptr)
			,_data(x)
		{}
	};


	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 * x)
			:_node(x)
		{}
		
	};
	

	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;

	private:
		node* _head;
	};

}

★ 迭代器实现 ★

这里的迭代器本质上是对原生指针的封装,所以其成员变量是节点的指针
里面的各个运算符重载也不过多赘述了,实际上就是对链表的操作,熟悉链表这些一看就理解了。

关键是,如何实现 const_iterator 呢?迭代器虽然是指向一个节点,但是实际上需要的是节点里存放的 _data 数据,节点里的其余两个指针无所谓,只是为了让迭代器++、-- 。所以,const_iterator 也只需要保证节点内的 _date 数据不被改变即可。至于指针,无所谓,依然可以进行插入、删除。

那么,实现 const_iterator ,对于迭代器而言,实际上只需要用 const 修饰 operator*() 重载(解引用)的返回值。但是这样就要重新定义一个 const_list_iterator 类,它和 __list_iterator 的区别仅仅是类名和 operator*() 重载的返回值。(不可以重载,因为仅仅是返回值不同。)
所以,这里要借鉴STL的方法,那就是传第二个模板参数,这个参数是 operator*() 的返回值类型。如下,在 list 类里面分别定义 iterator 和 const_iterator 。

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

	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 * x)
			:_node(x)
		{}

		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}

		self operator++(int)
		{
			self tmp = _node;
			_node = _node->_next;
			return tmp;
		}

		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return (&_node->_data);
		}

		self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		self operator--(int)
		{
			self tmp = _node;
			_node = _node->_prev;
			return tmp;
		}

		bool operator==(const self& x)
		{
			return _node == x._node;
		}

		bool operator!=(const self& x)
		{
			return _node != x._node;
		}
		
	};

此外, operator->() 也值得重载一下。否则,定义一个迭代器 it ,实际上就是定义了 __list_iterator 的一个对象,那么要通过 -> 访问数据,就要先 it._node ,拿到节点指针,然后才可以 it._node->_date 。这样子怪怪的,所以重载 operator->() ,返回值应该要是 T* ,即数据类型的指针,但是考虑到要区分是否是 const 迭代器,所以又要传入一个模板参数 Ptr ,以此来作为operator->() 的返回值

所以,最终在 list 中定义iterator 和 const_iterator 如下。第一个模板参数是数据类型,第二个模板参数是 operator*() 的返回值类型,第三个模板参数是 operator->() 的返回值类型。

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

★反向迭代器的实现★

反向迭代器利用了萃取技术。它是将 正向 迭代器进行封装,其成员变量就是一个正向迭代器,成员函数是根据需求,调用正向迭代器的成员函数。
例如,反向迭代器的 ++ ,实际上就是正向迭代器的 --。

实现了下面的反向迭代器的类,对于任意类型,都适用!!!为什么,因为这只不过是对正向迭代器的封装,反向迭代器里面的一切,都是对正向迭代器的成员函数的封装,只要类里面实现了正向迭代器,然后再加入下列代码,就可以实现反向迭代器!

    template<class Iterator, class Ref, class Ptr>
	struct ReverseIterator
	{
		typedef ReverseIterator<Iterator, Ref, Ptr> Self;
		Iterator _cur;

		ReverseIterator(Iterator it)
			:_cur(it)
		{}

		Ref operator*()
		{
			Iterator tmp = _cur;
			--tmp;
			return *tmp;
		}

		Self& operator++()
		{
			--_cur;
			return *this;
		}

		Self& operator--()
		{
			++_cur;
			return *this;
		}

		bool operator!=(const Self& s)
		{
			return _cur != s._cur;
		}
	};

构造函数、拷贝构造

初始化就是建立一个头节点。并且让它的 _prev 、 _next 指针都指向自己。

构造函数主要就是一个迭代器区间的构造,两个区间之内尾插数据即可。
拷贝构造可以复用构造函数,按照迭代器区间构造一个 list 对象 tmp,然后将这个 tmp 和 *this 互换一下,实际上就是互换两个对象的成员变量(指向链表头节点的指针)。 swap 函数的参数必须要是引用。

		void empty_init()
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
		}

		// 初始化一个list,先有一个哨兵位节点(双向带头循环链表)
		list()
		{
			empty_init();
		}

		void swap(list<T>& x)
		{
			std::swap(_head, x._head);
		}

		list(const list<T>& x)
		{
			empty_init();
			list<T> tmp(x.begin(), x.end()); // 这是构造函数
			swap(tmp);
		}

		template<class InputIterator>
		list(InputIterator start,InputIterator finish)
		{
			empty_init();
			while (start._node != finish._node)
			{
				push_back(*start);
				++start;
			}
		}

插入、删除

当实现 insert() 、erase() 之后,其他的插入、删除 都可以复用这两个函数。
insert() 、 erase() 其本质上也就是和链表类似的,并没有什么要注意的地方。

析构函数值得一看,因为 erase() 是删除单个节点,而析构是要删除整个链表,所以可以在析构里面复用 erase() ,最后也不要忘了要删除头节点。

		void push_back(const T& x)
		{
			//node* tail = _head->_prev;
			//node* tmp = new node(x);

			//tail->_next = tmp;
			//tmp->_prev = tail;

			//tmp->_next = _head;
			//_head->_prev = tmp;

			insert(end(), x);
		}
		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		void pop_front()
		{
			erase(begin());
		}

		void pop_back()
		{
			erase(--end());
		}
		
		void insert(iterator pos, const T& val) //在 pos 位置之前插入
		{
			node* cur = pos._node;
			node* prev = cur->_prev;

			node* tmp = new node(val);
			prev->_next = tmp;
			tmp->_prev = prev;
			tmp->_next = cur;
			cur->_prev = tmp;
		}

		iterator erase(iterator pos) // 删除当前位置的数据
		{
			assert(pos != end());
			node* prev = pos._node->_prev;
			node* next = pos._node->_next;
			delete pos._node;

			prev->_next = next;
			next->_prev = prev;
			return iterator(next);
		}

		~list<T>()
		{
			iterator it = begin();
			while (it != end())
			{
				erase(it++);//后置++ 在这里体现作用了,返回it,但是实际上 it 已经++
			}
			delete _head;
			_head = nullptr;
		}

其他成员函数

赋值重载的思想和拷贝构造类似,都是先构造出一个 list ,然后交换两个 list 对象的成员变量(也就是指向链表头节点的指针),这样就完成了赋值。由于参数没有传引用,所以相对于形参是实参的拷贝,使用 swap 不会影响实参。

其他的 begin() 、 end() 没啥好说的,begin() 返回的是指向第一个节点的迭代器,end 返回的是最后一个节点的下一个(即头节点)。

		list<T> operator=( list<T> x)
		{
			swap(x);
			return *this;
		}
		
		iterator begin()
		{
			return iterator(_head->_next);
		}

		iterator end() 
		{
			return iterator(_head);
		}

		// 这里 const 修饰的是 *this, *this 是 list 的一个对象,其成员变量是一个指针
		// 指针本身固然是不可以改变的,但是其指向的内容可以改变
		// 可是这样的话,如何实现 const_iterator 呢?
		const_iterator begin()const
		{
			return const_iterator(_head->_next);
		}

		const_iterator end()const
		{
			return const_iterator(_head);
		}

📕 源代码

iterator.h

#pragma once

namespace simulate_iterator
{
	template<class Iterator, class Ref, class Ptr>
	struct ReverseIterator
	{
		typedef ReverseIterator<Iterator, Ref, Ptr> Self;
		Iterator _cur;

		ReverseIterator(Iterator it)
			:_cur(it)
		{}

		Ref operator*()
		{
			Iterator tmp = _cur;
			--tmp;
			return *tmp;
		}

		Self& operator++()
		{
			--_cur;
			return *this;
		}

		Self& operator--()
		{
			++_cur;
			return *this;
		}

		bool operator!=(const Self& s)
		{
			return _cur != s._cur;
		}
	};
}

list.h


#pragma once
#include<iostream>
#include<assert.h>
using namespace std;

namespace simulate
{
	template<class T>
	struct __list_node
	{
		__list_node<T>* _prev;
		__list_node<T>* _next;
		T _data;

		__list_node(const T& x=T())
			:_prev(nullptr)
			,_next(nullptr)
			,_data(x)
		{}
	};


	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 * x)
			:_node(x)
		{}

		// 这里不需要写拷贝构造,直接用编译器自动生成的浅拷贝即可

		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}

		self operator++(int)
		{
			self tmp = _node;
			_node = _node->_next;
			return tmp;
		}

		Ref operator*()
		{
			return _node->_data;
		}

		Ptr operator->()
		{
			return (&_node->_data);
		}

		self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		self operator--(int)
		{
			self tmp = _node;
			_node = _node->_prev;
			return tmp;
		}

		bool operator==(const self& x)
		{
			return _node == x._node;
		}

		bool operator!=(const self& x)
		{
			return _node != x._node;
		}
		
	};
	

	template<class T>
	class list
	{
		typedef __list_node<T> node;
	public:
		typedef __list_iterator<T,T&,T*> iterator;
		//typedef __list_const_iterator<T> const_iterator;
		typedef __list_iterator<T, const T&,const T*> const_iterator;

		typedef simulate_iterator::ReverseIterator<iterator, T&, T*> reverse_iterator;
		typedef simulate_iterator::ReverseIterator<iterator, const T&, const T*> const_reverse_iterator;


		void empty_init()
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
		}

		// 初始化一个list,先有一个哨兵位节点(双向带头循环链表)
		list()
		{
			empty_init();
		}

		void swap(list<T>& x)
		{
			std::swap(_head, x._head);
		}

		list(const list<T>& x)
		{
			empty_init();
			list<T> tmp(x.begin(), x.end()); 
			swap(tmp);
		}

		template<class InputIterator>
		list(InputIterator start,InputIterator finish)
		{
			empty_init();
			while (start._node != finish._node)
			{
				push_back(*start);
				++start;
			}
		}

		list<T> operator=( list<T> x)
		{
			swap(x);
			return *this;
		}

		void push_back(const T& x)
		{
			//node* tail = _head->_prev;
			//node* tmp = new node(x);

			//tail->_next = tmp;
			//tmp->_prev = tail;

			//tmp->_next = _head;
			//_head->_prev = tmp;

			insert(end(), x);
		}

		void push_front(const T& x)
		{
			insert(begin(), x);
		}

		void pop_front()
		{
			erase(begin());
		}

		void pop_back()
		{
			erase(--end());
		}

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

		iterator end() 
		{
			return iterator(_head);
		}

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

		const_iterator end()const
		{
			return const_iterator(_head);
		}

		// 在 pos 位置的前一个插入数据
		void insert(iterator pos, const T& val)
		{
			node* cur = pos._node;
			node* prev = cur->_prev;

			node* tmp = new node(val);
			prev->_next = tmp;
			tmp->_prev = prev;
			tmp->_next = cur;
			cur->_prev = tmp;
		}

		iterator erase(iterator pos)
		{
			assert(pos != end());
			node* prev = pos._node->_prev;
			node* next = pos._node->_next;
			delete pos._node;

			prev->_next = next;
			next->_prev = prev;
			return iterator(next);
		}

		~list<T>()
		{
			iterator it = begin();
			while (it != end())
			{
				erase(it++);//后置++ 在这里体现作用了,返回it,但是实际上 it 已经++
			}

			delete _head;
			_head = nullptr;
		}

	private:
		node* _head;
	};

}




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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

努力努力再努力.xx

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

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

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

打赏作者

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

抵扣说明:

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

余额充值