list模拟实现

🔥list的结构

首先,在模拟实现list之前我们先了解一些list的结构。
在这里插入图片描述

list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。

因此,我们若要实现list,则首先需要实现一个结点类。而一个结点需要存储的信息有:要存储的数据、前一个结点的地址、后一个结点的地址

🔥节点类的实现

这里因为很多地方都要用到这个节点类,所以为了方便,定义成struct的类,这样,成员变量就都是共有的啦。

template <class T>
struct ListNode
{
	ListNode<T>* _prev;
	ListNode<T>* _next;
	T _data;

	ListNode(const T& data = T())
		:_prev(nullptr)
		, _next(nullptr)
		, _data(data)
	{}
};

🔥list的实现

有了节点类之后我们应该怎么,定义一个链表呢?

我们知道,链表其实就是用一个指向头节点的指针管理起来的,所以,我们可以定义一个list类,它的成员变量是一个指向头节点的指针,因为,list的底层是一个带头双向循环链表,所以这里的指针,应该指向,带有哨兵位的头节点。

🌈构造函数

list是一个带头双向循环链表,在构造一个list对象时,首先要申请一个头结点,并让其前驱指针和后继指针都指向自己。

list()
{
	_head = new node; //申请头结点
	_head->_next = _head; //头结点的后继指针指向自己
	_head->_prev = _head; //头结点的前驱指针指向自己
}

🌈拷贝构造

方式一:

我们先申请一个头结点,并让其前驱指针和后继指针都指向自己,然后将所给容器当中的数据,通过遍历的方式一个个尾插到新构造的容器后面即可。

因为这里的申请头节点的这个操作经常用所以我们可以封装成一个函数。

void empty_list()
{
	_head = new Node;
	_head->_prev = _head;
	_head->_next = _head;
}
list(const list<T>& lt)
{
	empty_list();

	for (const auto& e : lt)
	{
		push_back(e);
	}
}

方式二:

我们先实现一个使用迭代器区间构造的函数。

template <class Iterator>
list(Iterator first, Iterator last)
{
	empty_list();//不加会出问题

	while (first != last)
	{
		push_back(*first);
		++first;
	}
}

然后,我们先创建一个临时对象让他利用被拷贝对象的迭代器构造出来,然后再交换,被利用完后的临时对象会在栈帧结束后被清除。

我们先实现swap函数,很简单,交换头节点的指针就可以。

void swap(list<T>& tmp)
{
	std::swap(_head, tmp._head);
}
list(const list<T>& lt)
{
	empty_list();

	list<T> tmp(lt.begin(), lt.end());

	swap(tmp);
}

🔥赋值重载

通过编译器自动调用list的拷贝构造函数构造出来一个list对象,然后调用swap函数将原容器与该list对象进行交换即可。

list<T>& operator=(list<T> tmp)
{
	swap(tmp);

	return *this;
}

🌈析构函数

首先,我们可以调用clear函数清理容器当中的数据,然后将头结点释放,最后将头指针置空即可

void clear()
{
	iterator it = begin();

	while (it != end())
	{
		//it = erase(it);
		erase(it++);
	}
}
~list()
{
	clear();

	delete _head;
	_head = nullptr;
}

🔥insert 和 erase

🌈insert

insert函数的作用时在指定迭代器的位置之前插入一个数据。

在这里插入图片描述

iterator insert(iterator pos, const T& x)
{
	Node* node = pos._node;//记录当前节点
	Node* prev = node->_prev;//记录前驱节点

	Node* newnode = new Node(x);//记录要插入节点

	newnode->_prev = prev;
	newnode->_next = node;
	
	prev->_next = newnode;
	node->_prev = newnode;

	return iterator(newnode);
}

🌈erase

erase函数用来删除指定迭代器位置的数据。

在这里插入图片描述

iterator erase(iterator pos)
{
	assert(pos != end());//防止删除头节点
	
	Node* del = pos._node;//记录要删除节点
	Node* prev = del->_prev;//记录前驱节点
	Node* next = del->_next;//记录后续节点

	prev->_next = next;
	next->_prev = prev;

	return iterator(next);//返回所给迭代器pos的下一个迭代器,防止迭代器失效
}

🔥尾插,尾删,头插,头删

这里直接复用insert和erase即可。

// List的插入和删除
void push_back(const T& val) 
{ 
	insert(end(), val); 
}

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

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

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

🔥访问容器相关函数

T& front()
{
	return _head->_next->_data;
}

const T& front() const
{
	return _head->_next->_data;
}

T& back()
{
	return _head->_prev->_data;
}

const T& back() const
{
	return _head->_prev->_data;
}

🔥其他函数

size

size函数用于获取当前容器当中的有效数据个数,因为list是链表,所以需要通过遍历的方式统计有效数据的个数。

size_t size() const
{
	size_t count = 0;

	Node* pcur = _head->_next;
	while (pcur != _head)
	{
		count++;
		pcur = pcur->_next;
	}

	return count;
}

resize

resize函数的规则:

1,若当前容器的oldsize小于所给newsize,则尾插结点,直到oldsize等于newsize为止。
2,若当前容器的oldsize大于所给newsize,则只保留前newsize个有效数据。

void resize(size_t newsize, const T& data = T())
{
	size_t oldsize = size();

	if (newsize <= oldsize)
	{
		while (newsize != oldsize)
		{
			pop_back();
			oldsize--;
		}
	}
	else
	{
		while (oldsize != newsize)
		{
			push_back(data);
			oldsize++;
		}
	}
}

🔥迭代器实现

在这里插入图片描述
对于,vector迭代器来说我们可以使用原生指针来实现,因为vector的空间时连续的,_start时指向第一个数据的指针,_start++就指向下一个位置了

但是对于list来说,它的数据不是连续存储的而是通过一个一个节点通过指针连接到一起的,所以,_head++,并不能到下一个数据的位置,但是可以通过_head = _head->_next实现,所以这里我们可以使用节点的指针单独封装一个类,通过运算符重载模拟指针的行为。

template <class T,class Ref,class Ptr>
struct ListIterator
{
	typedef ListNode<T> Node;
	Node* _node;

	typedef ListIterator<T,Ref,Ptr> self;

	ListIterator(Node* node)
		:_node(node)
	{}

	self& operator++()
	{
		_node = _node->_next;

		return *this;
	}

	self operator++(int)
	{
		self tmp(*this);
		_node = _node->_next;

		return tmp;
	}

	self& operator--()
	{
		_node = _node->_prev;

		return *this;
	}

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

		return tmp;
	}

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

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

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

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

🌈迭代器类的模板参数说明

template<class T, class Ref, class Ptr>

在list的模拟实现当中,我们typedef了两个迭代器类型,普通迭代器和const迭代器

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

所以,迭代器类的模板参数列表当中的RefPtr分别代表的是引用类型和指针类型。

当我们使用普通迭代器时,编译器就会实例化出一个普通迭代器对象;当我们使用const迭代器时,编译器就会实例化出一个const迭代器对象。

🌈begin和end

begin函数返回的是第一个有效数据的迭代器,end函数返回的是最后一个有效数据的下一个位置的迭代器。

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);
}

全部代码实现

namespace hb
{
	template <class T>
	struct ListNode
	{
		ListNode<T>* _prev;
		ListNode<T>* _next;
		T _data;

		ListNode(const T& data = T())
			:_prev(nullptr)
			, _next(nullptr)
			, _data(data)
		{}
	};

	template <class T,class Ref,class Ptr>
	struct ListIterator
	{
		typedef ListNode<T> Node;
		Node* _node;

		typedef ListIterator<T,Ref,Ptr> self;

		ListIterator(Node* node)
			:_node(node)
		{}

		self& operator++()
		{
			_node = _node->_next;

			return *this;
		}

		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_next;

			return tmp;
		}

		self& operator--()
		{
			_node = _node->_prev;

			return *this;
		}

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

			return tmp;
		}

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

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

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

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

	template <class T>
	struct ListConstIterator
	{
		typedef ListNode<T> Node;
		Node* _node;

		typedef ListConstIterator<T> self;

		ListConstIterator(Node* node)
			:_node(node)
		{}

		self& operator++()
		{
			_node = _node->_next;

			return *this;
		}

		self operator++(int)
		{
			self tmp(*this);
			_node = _node->_next;

			return tmp;
		}

		self& operator--()
		{
			_node = _node->_prev;

			return *this;
		}

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

			return tmp;
		}

		const T& operator*()
		{
			return _node->_data;
		}

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

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

		const T* operator->()
		{
			return &_node->_data;
		}
	};


	template <class T>
	class list
	{
		typedef ListNode<T> Node;
	public:
		typedef ListIterator<T,T&,T*> iterator;
		typedef ListIterator<T,const T&,const T*> const_iterator;
		//typedef ListConstIterator<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);
		}

		void empty_list()
		{
			_head = new Node;
			_head->_prev = _head;
			_head->_next = _head;
		}

		list()
		{
			empty_list();
		}

		~list()
		{
			clear();

			delete _head;
			_head = nullptr;
		}

		void clear()
		{
			iterator it = begin();

			while (it != end())
			{
				//it = erase(it);
				erase(it++);
			}
		}



		/*list(const list<T>& lt)
		{
			empty_list();

			for (const auto& e : lt)
			{
				push_back(e);
			}
		}*/

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

		template <class Iterator>
		list(Iterator first, Iterator last)
		{
			empty_list();//不加会出问题

			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		list(const list<T>& lt)
		{
			empty_list();

			list<T> tmp(lt.begin(), lt.end());

			swap(tmp);
		}

		list<T>& operator=(list<T> tmp)
		{
			swap(tmp);

			return *this;
		}

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

			newnode->_prev = tail;
			newnode->_next = _head;

			tail->_next = newnode;
			_head->_prev = newnode;*/

			insert(end(),x);
		}

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

		void pop_back()
		{

			erase(--end());
		}

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

		iterator insert(iterator pos, const T& x)
		{
			Node* node = pos._node;
			Node* prev = node->_prev;

			Node* newnode = new Node(x);

			newnode->_prev = prev;
			newnode->_next = node;
			
			prev->_next = newnode;
			node->_prev = newnode;

			return iterator(newnode);
		}

		iterator erase(iterator pos)
		{
			assert(pos != end());
			

			Node* del = pos._node;
			Node* prev = del->_prev;
			Node* next = del->_next;

			prev->_next = next;
			next->_prev = prev;

			return iterator(next);
		}

		bool empty() const
		{
			return _head == _head->_next;
		}

		T& front()
		{
			return _head->_next->_data;
		}

		const T& front() const
		{
			return _head->_next->_data;
		}

		T& back()
		{
			return _head->_prev->_data;
		}

		const T& back() const
		{
			return _head->_prev->_data;
		}

		size_t size() const
		{
			size_t count = 0;

			Node* pcur = _head->_next;
			while (pcur != _head)
			{
				count++;
				pcur = pcur->_next;
			}

			return count;
		}

		void resize(size_t newsize, const T& data = T())
		{
			size_t oldsize = size();

			if (newsize <= oldsize)
			{
				while (newsize != oldsize)
				{
					pop_back();
					oldsize--;
				}
			}
			else
			{
				while (oldsize != newsize)
				{
					push_back(data);
					oldsize++;
				}
			}
		}
		
	private:
		Node* _head;
};
  • 84
    点赞
  • 75
    收藏
    觉得还不错? 一键收藏
  • 60
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值