list 的模拟实现

list 前言

1、list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
2.、list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
3、与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好,因为插入删除时不用挪动数据。
4、与其他序列式容器相比,list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息,比如存储前驱和后继指针。

list 的模拟实现

结点的定义

根据双向链表,我们不仅需要存储结点的值,还需要存储前驱指针(_prev)和后继指针(_next),前驱指针指向当前结点的前一个结点,后继指针指向当前结点的下一个结点。

    template<class T>
	struct ListNode
	{
		ListNode(const T& val = T())
			:_prev(nullptr)
			,_next(nullptr)
			,_val(val)
		{}
		ListNode<T>* _prev;//前驱
		ListNode<T>* _next;//后继
		T _val;
	};

链表的定义

构造函数:

链表中没有结点时,链表的哨兵位的前驱和后继指针都指向自己。

特别注意的是,哨兵位的_val不可以存储链表的结点的个数,因为我们不知道链表里面的数据是什么类型,假设是char类型的链表,char能表示的最大值是127,如果链表里面结点的个数超过127,即超出了char能表示的取值范围,则结点个数的记录没有意义。

template<class T>
class list
{
	typedef ListNode<T> Node;//不放在public里面,避免暴露在类外面
public:
    void empty_init()//用于初始化哨兵位
	{
		_head = new Node(T());
		_head->_next = _head;
		_head->_prev = _head;
	}
	list()
	{
		empty_init();//复用
	}
private:
	Node* _head;//哨兵位
};
迭代器: 
为什么要封装迭代器:

list 和 vector 的迭代器有所不同:

1、vector 的数据在底层是连续存储的,假设下面的 it 是vector 的迭代器,*it 便可以得到 it 位置的数据(迭代器的本质是指针,对指针解引用就可以拿到指针指向的内容),++it 就可以访问下一个结点(因为底层是连续的)

2、由于 list 的底层不是连续的,++it 不能够访问下一个结点,而且一个结点里面存储了结点的数据、前驱和后继指针,*it 不能直接拿到我们想要的数据,所以我们需要对 list 的迭代器进行封装,以满足我们的需求

list<int>::iterator it = l.begin();
while (it != l.end())
{
	cout << *it<< " ";
	++it;
}
迭代器的实现: 

为什么不可以直接在 list 类里面对迭代器(指针,即 Node*)的 ++ 和解引用进行运算符重载,而是单独写迭代器的类?

因为 Node* (指针)是内置类型,内置类型不可以运算符重载,自定义类型才可以运算符重载,所以我们需要把 Node* 封装为 自定义类型,方便进行运算符重载。

其中特别关注 -> 的运算符重载,返回的是结点的_val 的地址。

正向迭代器:
    template<class T,class Ref,class Ptr>//Ref为引用(T&),Ptr为指针(T*)
	struct __list_iterator
	{
		typedef ListNode<T> Node;
		typedef __list_iterator<T, Ref, Ptr> self;//把迭代器重命名为self
		Node* _node;//把迭代器指向的结点封装起来

		__list_iterator(Node* x)
			:_node(x)//方便隐式类型转换
		{ }
        self& operator++()//前置++,返回迭代器,返回值带引用,减少拷贝
		{
			//return _node->_next;
            //这个写法不行,没有满足前置++的先++后使用,即_node没有指向下一个结点

			_node = _node->_next;
			return *this;//this指针指的是迭代器(包含结点)
		}
        Ref operator*()//解引用返回结点的_val的引用
		{
			return _node->_val;
		}
        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;//this里的node往前走
			return tmp;
		}
        Ptr operator->()//箭头返回返回结点的_val的地址
		{
			return &_node->_val;
		}
        bool operator==(const self& s)
		{
			return _node == s._node;
		}
		bool operator!=(const self& s)
		{
			return _node != s._node;
		}
    };
反向迭代器:

反向迭代器和正向迭代器在结构上具有对称性。

 我们可以复用正向迭代器来实现反向迭代器。

为了让正向迭代器和反向迭代器在下面的 while 的使用规则上一致,反向迭代器的 ++it 实际上是走向当前结点的前驱。

*it 中,我们需要先创建变量 tmp,因为反向迭代器是从 _head 开始的,哨兵位的值不是我们想要的值,我们需要 -- tmp ,使 tmp 指向当前结点的前驱,再去访问 tmp 里面的值

list<int>::iterator it = l.begin();
while (it != l.end())
{
	cout << *it<< " ";
	++it;
}
    template<class Iterator,class Ref,class Ptr>
	class Reverse_Iterator
	{
		//反向迭代器复用迭代器的类
		Iterator _it;
		typedef Reverse_Iterator<Iterator, Ref, Ptr> self;
        //同样把反向迭代器重命名为 self

		Reverse_Iterator(Iterator it)
			:_it(it)
		{  }//传一个迭代器,拷贝出一个迭代器

		self& operator++()
		{//++it,反向迭代器的++是走前驱,即访问前一个元素
			--_it;
			return *this;
		}
		self& operator--()
		{
			++_it;
			return *this;
		}
		bool operator!=(self& s)
		{
			return _it != s._it;
		}
		Ref operator*()
		{//解引用
			Iterator tmp(_it);
			return *(--tmp);
			//根据反向迭代器的结构,需要先--指向当前位置的前一个结点
			//再复用正向迭代器的解引用,访问节点的值
		}

		Ptr operator->()
		{
			//return &(*(_it));
			return &(operator*());
		}
	};
 迭代器相关的实现:

重命名时,把模板实例化,Ref 实例化为 T&,Ptr 实例化为 T*。

        typedef __list_iterator<T, T&, T*> iterator;//要放在public里面,iterator才可以在类外面访问
		typedef __list_iterator<T, const T&, const T*> const_iterator;
		typedef Reverse_Iterator<iterator, T&, T*> reverse_iterator;
		typedef Reverse_Iterator<iterator,const T&,const T*> const_reverse_iterator;
		reverse_iterator rbegin()
		{
			return _head;
		}
		reverse_iterator rend()
		{
			return _head->_next;
		}
		const_reverse_iterator rbegin()const
		{
			return _head;
		}
		const_reverse_iterator rend()const
		{
			return _head->_next;
		}
        iterator begin()
		{
			return _head->_next;
		}
		iterator end()
		{
			return _head;//最后一个结点的下一个结点
		}
		const_iterator begin()const//记得写参数后面的const
		{
			return _head->_next;//权限缩小
		}
		const_iterator end()const
		{
			return _head;
		}
插入 insert:

假设要插入的结点为 newnode,根据图示可以看出,我们需要修改pos、pos的前一个结点和newnode 的前驱和后继指针,便可以完成插入操作。由于把迭代器封装起来,访问 pos 的前一个结点时,先用 . 操作符访问 pos 迭代器指向的结点,进而获得 pos 位置的前一个结点 prev。insert 函数的返回值为新插入结点的迭代器(隐式类型转换)

iterator insert(iterator pos, const T& val)//在pos之前插入
{
	Node* prev = pos._node->_prev;
	//因为函数参数用了迭代器这个类,而不是像之前直接访问pos位置的指针
	//我们需要用.来访问迭代器pos里面的成员_node
	Node* newnode = new Node(val);
	newnode->_next = pos._node;
	newnode->_prev = prev;
	pos._node->_prev = newnode;
	prev->_next = newnode;

	return newnode;
}
尾插 push_back:
void push_back(const T& val)
{
	insert(end(), val);
}
头插 push_front:
void push_front(const T& val)
{
	insert(begin(), val);
}
删除 erase:

erase 的函数返回值为 pos 位置的下一个节点。删除结点的时候需要修改 posprev 的后继和posnext 的前驱指针,否则链表的连接会出现问题。

iterator erase(iterator pos)
{
	assert(pos != end());
	Node* posprev = pos._node->_prev;
	Node* posnext = pos._node->_next;
	posprev->_next = posnext;
	posnext->_prev = posprev;

	delete pos._node;
	return posnext;
}
尾删 pop_back + 头删 pop_front:

由于 end() 是指向链表最后一个结点的下一个结点,需要 --end() 才可以访问链表的最后一个结点。

void pop_back()
{
	erase(--end());
}
void pop_front()
{
	erase(begin());
}
拷贝构造:

拷贝构造的参数要传引用,如果不传引用,传参后又调用拷贝构造来形成形参,形参又调用拷贝构造,因此造成无穷递归。

list(list<T>& l)//拷贝构造要传引用,否则会无穷递归
{
	//拷贝之前需要把先处理哨兵位
	empty_init();
	for (const auto& e : l)
	{
		push_back(e);//用迭代器遍历并尾插
	}
}
析构函数:
        void clear()
		{
			//clear不处理哨兵位
			iterator it = begin();//迭代器已经封装好了,不用像上面那么写
			while (it != end())
			{
				it = erase(it);//需要迭代 it
			}
		}
		~list()
		{
			clear();//复用
			delete _head;//处理哨兵位
			_head = nullptr;//delete只是清掉内容,但是指针还是在的,指针也要处理
		}
其他函数:

= 的运算符重载函数不可以传引用,传了引用之后,= 左右的链表就交换了,不符合 = 的原则。

        void swap(list<T>& s)
		{
			std::swap(_head, s._head);
		}
		list<T>& operator=(list<T> s)
		{//list1 = list2
			swap(s);
			return *this;
		}

全部代码合并:

    template<class T>
	struct ListNode
	{
		ListNode(const T& val = T())
			:_prev(nullptr)
			,_next(nullptr)
			,_val(val)
		{}
		ListNode<T>* _prev;//前驱
		ListNode<T>* _next;//后继
		T _val;
	};
	template<class T,class Ref,class Ptr>//Ref为引用,Ptr为指针
	struct __list_iterator
	{
		typedef ListNode<T> Node;
		typedef __list_iterator<T, Ref, Ptr> self;
		Node* _node;

		__list_iterator(Node* x)
			:_node(x)
		{ }
		//写构造函数,在 iterator end(){ return _head; }时才可以隐式类型转换,否则返回值和返回值类型不匹配

		self& operator++()//前置++,返回迭代器,返回值带引用,减少拷贝
		{
			//return _node->_next;
			_node = _node->_next;
			return *this;//this指针指的是迭代器
		}
		self operator++(int)//后置++
		{
			//return _node->_next;
			/*Node* tmp = _node;
			_node = _node->_next;
			return tmp;*/
			self tmp(*this);//调构造函数
			_node = _node->_next;
			return tmp;
		}
		self& operator--()//前置--
		{
			//return _node->_prev;
			_node = _node->_prev;
			return *this;
		}
		self operator--(int)//后置--
		{
			//return _node->_prev;
			/*Node* tmp = _node;
			_node = _node->_prev;
			return tmp;*/

			self tmp(*this);
			_node = _node->_prev;//this里的node往前走
			return tmp;
		}
		Ref operator*()//解引用返回结点的_val的引用
		{
			return _node->_val;
		}
		Ptr operator->()//箭头返回返回结点的_val的地址
		{
			return &_node->_val;
		}
		bool operator==(self& s)
		{
			return _node == s._node;
		}
		bool operator!=(const self& s)
		{
			return _node != s._node;
		}
	};
	template<class Iterator,class Ref,class Ptr>
	class Reverse_Iterator
	{
		//反向迭代器复用迭代器的类
		Iterator _it;
		typedef Reverse_Iterator<Iterator, Ref, Ptr> self;

		Reverse_Iterator(Iterator it)
			:_it(it)
		{  }//传一个迭代器,拷贝出一个迭代器

		self& operator++()
		{//++it,反向迭代器的++是走前驱,即访问前一个元素
			--_it;
			return *this;
		}
		self& operator--()
		{
			++_it;
			return *this;
		}
		bool operator!=(self& s)
		{
			return _it != s._it;
		}
		Ref operator*()
		{//解引用
			Iterator tmp(_it);
			return *(--tmp);
			//根据反向迭代器的结构,需要先--指向当前位置的前一个结点
			//再复用正向迭代器的解引用,访问节点的值
		}

		Ptr operator->()
		{
			//return &(*(_it));
			return &(operator*());
		}
	};
	template<class T>
	class list
	{
		typedef ListNode<T> Node;//不放在public里面,避免暴露在外面		
	public:
		typedef __list_iterator<T, T&, T*> iterator;//要放在public里面,iterator才可以在类外面访问
		typedef __list_iterator<T, const T&, const T*> const_iterator;
		typedef Reverse_Iterator<iterator, T&, T*> reverse_iterator;
		typedef Reverse_Iterator<iterator,const T&,const T*> const_reverse_iterator;
		reverse_iterator rbegin()
		{
			return _head;
		}
		reverse_iterator rend()
		{
			return _head->_next;
		}
		const_reverse_iterator rbegin()const
		{
			return _head;
		}
		const_reverse_iterator rend()const
		{
			return _head->_next;
		}
		void empty_init()
		{
			_head = new Node(T());
			_head->_next = _head;
			_head->_prev = _head;
		}
		list()
		{
			empty_init();
		}
		iterator begin()
		{
			return _head->_next;
		}
		iterator end()
		{
			return _head;//最后一个结点的下一个结点
		}
		const_iterator begin()const//记得写参数后面的const
		{
			return _head->_next;//权限缩小
		}
		const_iterator end()const
		{
			return _head;
		}
		
		iterator insert(iterator pos, const T& val)//在pos之前插入
		{
			Node* prev = pos._node->_prev;
			//因为函数参数用了迭代器这个类,而不是像之前的pos位置的指针
			//我们需要用.来访问迭代器pos里面的成员_node
			Node* newnode = new Node(val);
			newnode->_next = pos._node;
			newnode->_prev = prev;
			pos._node->_prev = newnode;
			prev->_next = newnode;

			return newnode;
		}
		void push_back(const T& val)
		{
			insert(end(), val);
		}
		void push_front(const T& val)
		{
			insert(begin(), val);
		}
		
		iterator erase(iterator pos)
		{
			assert(pos != end());
			Node* posprev = pos._node->_prev;
			Node* posnext = pos._node->_next;
			posprev->_next = posnext;
			posnext->_prev = posprev;

			delete pos._node;
			return posnext;
		}
		void pop_back()
		{
			erase(--end());
		}
		void pop_front()
		{
			erase(begin());
		}
		void clear()
		{
			//clear不处理哨兵位
			//怎么调用迭代器?
			//list<T>::iterator it = begin();
			//while (it != end())
			//{
				//pop_front(it); 头删里面不用传参
				//++it;
			//}
			iterator it = begin();//迭代器已经封装好了,不用像上面那么写
			while (it != end())
			{
				it = erase(it);
			}
		}
		~list()
		{
			clear();
			delete _head;
			_head = nullptr;//delete只是清掉内容,但是指针还是在的,指针也要处理
		}
		list(list<T>& l)//拷贝构造要传引用,否则会无穷递归
		{
			//拷贝之前需要把先处理哨兵位
			empty_init();
			for (const auto& e : l)
			{
				push_back(e);
			}
		}
		void swap(list<T>& s)
		{
			std::swap(_head, s._head);
		}
		list<T>& operator=(list<T> s)
		{//list1 = list2
			swap(s);
			return *this;
		}
	private:
		Node* _head;//哨兵位的头结点
	};

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值