【list的模拟实现】

1、list的迭代器底层实现
在这里插入图片描述

一个类型去封装结点的指针,构成一个自定义类型,然后重载*和++等运算符,使其可以像指针一样进行使用。

2、模拟实现list

刚开始实现的初步工作就是实现一个双向循环链表,并且指向自己。

(1)结点类型定义

//结点类型
	template<class T>
	struct _list_node
	{
		_list_node<T>* _next;
		_list_node<T>* _prev;
		T _data;

		_list_node(const T& x=T())//注意这里的无参构造
			:_data(x)
			, _next(nullptr)
			, _prev(nullptr)
		{}
	};

(2)迭代器实现

//迭代器
	//_list_iterator<T,T&,T*>
	//_list_iterator<T,const T&,const T*>
	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* node)//封装一个节点类型的指针让其成为迭代器
			:_node(node)
		{}
		//*it
		Ref operator*()//返回值是T&或constT&
		{
			return _node->_data;
		}
		//++it
		Self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		//it++
		Self operator++(int)
		{
			Self tmp(*this);
			++(*this);
			return tmp;
		}
		//--
		Self operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		Self operator--(int)
		{
			Self tmp(*this);
			-- (*this);
			return tmp;
		}
	    //!=
		bool operator!=(const Self& it)//传进来的是一个迭代器
		{
			return _node != it._node;
		}

		//->
		Ptr operator->()//返回类型是T*或const T*
		{
			return &_node->_data;
		}

	}
	
  • 通过设计三个参数,来区分const_iterator和普通Iterator;且实现就是将结点类型的指针进行封装,使其具有指针的基本功能即可。
    ->重载的补充

在这里插入图片描述(3)list内的begin()和end()

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;//const迭代器
		//根据传入的类型的不同,来确定是const迭代器还是普通迭代器

		//要写成普通版本和const版本
		iterator begin()
		{
			return iterator(_head->_next);
		}
		iterator end()
		{
			return iterator(_head);
		}
		const_iterator begin()const//返回的是const迭代器
		{
			return const_iterator(_head->_next);
		}
		const_iterator end()const
		{
			return const_iterator(_head);
		}
private:
		Node* _head;//头结点

	};

其中通过typdef来定义iterator和const_iterator,传入的类型不同即Ref和Ptr类型不同。
在这里插入图片描述
在这里插入图片描述

(4)构造函数

list()
		{
			_head = new Node;//申请头节点
			_head->_next = _head;
			_head->_prev = _head;
		}

申请头节点,并且初始状态让自身的两个指针都指向自己(_prev和_next),这就是带头的双向循环链表。

(5)拷贝构造和赋值运算符重载

//拷贝构造和赋值运算符重载
		list(const list<T>& it)//不存在的
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;

			const_iterator l = it.begin();
			while (l!=it.end())
			{
				push_back(*l);
				++l;
			}

		}
		list<T>& operator=(const list<T>& it)//赋值运算符重载有自己的头结点
		{
			if (this != &it)//不用自己给自己赋值
			{
				clear();
				for (auto &i : it)
				{
					push_back(i);
				}
			}
			return *this;
		}
		//新写法
		list<T>& operator=(list<T>& it)
		{
			swap(_head, it._head);
			return *this;
		}

(1)拷贝构造是一个构造过程,这个对象还不存在,所以可以
_head=new Node; _head->_next = _head; _head->_prev = _head;
先新建一个带头双向循环链表,再进行每个节点的值的拷贝插入。
(2)赋值运算符重载,是在两个对象都已经构造好都有(或一个有一个没有)一定的元素的情况下用到的。这个时候只需要清空要拷贝数据的list,然后再将另一个的数据完全插入进去即可。
(3)赋值运算符的新写法解析:

list<T>& operator=(list<T>& it)
		{
			swap(_head, it._head);
			return *this;
		}

传入的是一个临时变量,交换了需要被赋值的list和传入变量的的头结点的指向,交换完成后,原传入的临时变量带着原来_head的指向空间被销毁,留下来的是赋值成功的list。

(6)insert

void insert(iterator pos, const T& x)//在pos位置前进行插入
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);

			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = cur;
			cur->_prev = newnode;
		}

在这里插入图片描述

(7)erase

void erase(iterator pos)
		{
			assert(pos != end());//头节点不能删
			
			Node* cur = pos._node;
			Node*prev = cur->_prev;
			Node*next = cur->_next;
			delete cur;
			
			prev->_next = next;
			next->_prev = prev;
		}

在这里插入图片描述
(8)push_back/push_front/pop_back/pop_front

//push_back
		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;*/
			insert(end(), x);
		}
		void pop_back()
		{
			//erase(iterator(_head->_prev));
			erase(--end());
		}
		void push_front(const T& x)
		{
			insert(begin(), x);
		}
		void pop_front()
		{
			erase(begin());
		}

因基本逻辑相同,所以这里利用insert和erase实现,提高代码复用率

(9)析构函数 和 clear()

~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}
		//clear
		void clear()
		{
			iterator it = begin();
			while (it!=end())
			{
				erase(it++);
			}
		}

clear()只留头结点,而析构函数最后也要将头结点销毁掉。

具体代码:https://github.com/Wilingpz/sunny/tree/master/list%E7%9A%84%E6%A8%A1%E6%8B%9F%E5%AE%9E%E7%8E%B0/list%E7%9A%84%E6%A8%A1%E6%8B%9F%E5%AE%9E%E7%8E%B0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值