List的模拟实现(erase的迭代器失效)

本文探讨了如何在链表中设计和实现迭代器,包括__list_iterator和__list_const_iterator模板,模拟指针操作并支持const和非const版本。重点介绍了构造、拷贝构造、赋值和删除等关键方法,以及在list类中的应用和使用实例。
摘要由CSDN通过智能技术生成

链表中每个节点的结构

每个节点都有一个prev,next,以及data

//list节点的结构
	template<class T>
	struct __list_node
	{
		//形参这样写的原因是,万一T是自定义类型,list<string>
		__list_node(const T& x = T())
		:_prev(nullptr)
		, _next(nullptr)
		, _data(x)
		{}
		__list_node<T>* _prev;
		__list_node<T>* _next;
		T _data;
	};

链表的迭代器认识

vector和string的迭代器是原生指针,无论是解引用还是++,都可以得到自己想要的值。
链表的迭代器不能是原生指针了,链表的解引用是想要链表的值,++是想到下一个节点,那么假如是原生指针的话解引用是一个结构体,++是下一个结构体。等等,所以我们要通过重载运算符来模拟我们想要的行为。所以迭代器也被成为一种设计模式。对于不同的类,不需要知道你的内部实现,就可以对你进行访问和修改。

而对于赋值重载,需要实现深拷贝吗,不需要,我就是想要你那个节点的值。
析构函数,不需要自己实现,因为迭代器是访问数据,修改数据,并不是需要把那个节点删除.

源码中为什么迭代器的类模板要template<class T,class Ref,class Ptr>
命名为Ref,显然是想要你传引用,
命名为Ptr,显然是想要你传指针。

先来看看之前学的,string有两种迭代器,支持const对象迭代

typedef char* iterator,
typedef const char* iterator

vector迭代器有两种

typedef T* iterator
typedef const T* iterator

那么这里的List呢,由于原生指针的不适用,我们实现了一个迭代器类,里面封装了操作来模拟指针的操作。
照上面的想法,实现两种迭代器,还要实现两个类.

    template<class T>
	struct __list_iterator
	{
		typedef __list_iterator<T> self;
		//相同操作
		//访问变量操作
		T& operator*()
		{
			return _node->_data;
		}
    }
    template<class T>
	struct __list_const_iterator
	{
		typedef __list_const_iterator<T> self;
		//相同操作
		//访问变量操作
		const T& operator*()
		{
			return _node->_data;
		}
    }
    

而这两个类中部分操作一样,只是const迭代器类中,访问变量的那个函数多加了一个const修饰返回值让他不被修改,仅此而已.
假如List想要使用

template <class T>
class list
{
  public:
  typedef __list_iterator<T> iterator;
  typedef __list_const_iterator<T> const_iterator;
  
  
  ...
  private:
  
  ...
}

两个类大部分代码冗余,能不能用一个类呢,STL中提供了另一种很强的操作。


template<class T,class Ref,class Ptr>
struct __list_iterator
	{
		typedef __list_iterator<T> self;
		//操作
		//访问变量操作
		Ref& operator*()
		{
			return _node->_data;
		}
    }

List中使用

template <class T,class Ref,class Ptr>
class list
{
  public:
  
  typedef __list_iterator<T,T&,T*> iterator;
  typedef __list_const_iterator<T,const T&,const T*> const_iterator;
  
  ...
  private:
  
  ...
}

这也正体现了类设计复用。而传入ptr也是为了考虑当一个自定义类型没有重载输出时,用.或者指针直接输出。本来应该是it.operator->()->x即it->->x,为了易读,将它变成it->x
在这里插入图片描述

迭代器实现

	//list迭代器的结构
	//原生指针满足不了需求,所以需要自己写一个迭代器
	template<class T,class Ref,class Ptr>
	//在list插入操作时需要访问node来确定位置的前后节点,所以成员变量得是公有的
	struct __list_iterator
	{
		typedef __list_iterator<T,Ref,Ptr> self;
		typedef __list_node<T> node;
         
		//节点构造
		__list_iterator(node* node)
			:_node(node)
		{}
		//不用实现赋值,拷贝构造,默认的浅拷贝即可。不用实现析构,默认的就可以(总不能把人家节点析构了吧)
		//对于迭代器,节点指向同一个位置就相等,地址相等
		bool operator!=(const self& s)const
		{
			return _node != s._node;
		}
		bool operator==(const self& s)const
		{
			return !(_node != s._node);
		}
		Ref operator*()const
		{
			return _node->_data;
		}
		//ptr为T*,  _node->data为T类型,假如为自定义类型就可以访问他的成员变量了
		Ptr operator->()const
		{
			return &(_node->_data)
		}
		//前置++
		self& operator++()
		{
			//前置++,_node已经改变
			 _node=_node->_next;
			 return *this;
		}
		//后置++
		self operator++(int)
		{
			
			self s(*this);
			_node = _node->_next;
			return s;

		}
		//前置--
		self operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		//后置--
		self operator--(int)
		{
			self s(*this);
			_node = _node->_prev;
			return s;
		}
		
		node* _node;
	};

整个链表的结构

每个链表默认给一个带头节点。

迭代器接口

迭代器就可以在这使用了

		typedef __list_node<T> node;
	public:
		typedef __list_iterator<T,T&,T*> iterator;
		typedef __list_iterator<T, const T&, const T*> const_iterator;
//迭代器接口
		iterator begin()
		{
			//隐式类型转化成迭代器。
			return _head->_next;
			//return iterator(_head->_next);
			//iterator it(_head->next)  return it;
		}
		
		iterator end()
		{
			//最后一个节点的下一个位置,即_head.
			return _head;
		}
		const_iterator begin()const
		{
			return _head->_next;
		}
		const_iterator end()const
		{
			return _head;
		}

构造函数

无参构造

循环起来,开始啥都没有指向自己,node有默认构造函数

list()
		{
			//会调用节点的构造函数初始化
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
			
		}

迭代器拷贝构造

	//迭代器拷贝构造
     template<class inputiterator>
	 list(inputiterator first, inputiterator last)
	 {
		 _head = new node;
		 _head->_next = _head;
		 _head->_prev = _head;

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

拷贝构造

void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_stage, v._end_of_stage);
		}

利用已有的迭代器进行构造。
忘记了,竟然还想想写赋值重载那样形参写成传值调用,你拷贝构造。写传值就引发无穷递归了

list(const list<T>& l)
		{
			//不初始化头节点,直接和别人交换,就会导致执行temp的析构函数(析构了随机值)时崩溃
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;

			list<T> temp(l.begin(),l.end());
			std::swap(_head, temp._head);
		}

赋值重载

//赋值重载
		list<T>& operator=(list<T> l)
		{
		std::swap(_head,l.head);
		return *this;
		}

析构函数

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

插入

插入不会引起迭代器失效,但是需要返回值来控制让pos指向新插入的那一个节点。

	//在pos之前插入
		iterator insert(iterator pos,const T& x)
		{
			node* newnode = new node(x);
			node* cur = pos._node;
			node* prev = cur->_prev;
		
			prev->_next = newnode;
			newnode->_prev = prev;

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

			//返回新插入的位置
			return newnode;
			
		}

删除

删除一定会引起当前迭代器失效,但不会影响其他,因为空间不连续。所以需要返回值来指向原来pos的下一个位置。

在这里插入图片描述

//删除
		iterator erase(iterator pos)
		{
			//不能删头
			assert(pos != end());
			node* cur = pos._node;
			node* prev = cur->_prev;
			node* next = cur->_next;
			prev->_next = next;
			next->_prev = prev;
			delete cur;

			return next;
		}

链表实现代码



#include<iostream>
#include<assert.h>
#include<algorithm>

namespace zjn
{
	//list节点的结构
	template<class T>
	struct __list_node
	{
		//形参这样写的原因是,万一T是自定义类型,list<string>
		__list_node(const T& x = T())
		:_prev(nullptr)
		, _next(nullptr)
		, _data(x)
		{}
		__list_node<T>* _prev;
		__list_node<T>* _next;
		T _data;
	};
	//list迭代器的结构
	//原生指针满足不了需求,所以需要自己写一个迭代器
	template<class T,class Ref,class Ptr>
	//在list插入操作时需要访问node来确定位置的前后节点,所以成员变量得是公有的
	struct __list_iterator
	{
		typedef __list_iterator<T,Ref,Ptr> self;
		typedef __list_node<T> node;
         
		//节点构造
		__list_iterator(node* node)
			:_node(node)
		{}
		//不用实现赋值,拷贝构造,默认的浅拷贝即可。不用实现析构,默认的就可以(总不能把人家节点析构了吧)
		//对于迭代器,节点指向同一个位置就相等,地址相等
		bool operator!=(const self& s)const
		{
			return _node != s._node;
		}
		bool operator==(const self& s)const
		{
			return !(_node != s._node);
		}
		Ref operator*()const
		{
			return _node->_data;
		}
		//ptr为T*,  _node->data为T类型
		Ptr operator->()const
		{
			return &(_node->_data)
		}
		//前置++
		self& operator++()
		{
			//前置++,_node已经改变
			 _node=_node->_next;
			 return *this;
		}
		//后置++
		self operator++(int)
		{
			
			self s(*this);
			_node = _node->_next;
			return s;

		}
		//前置--
		self operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		//后置--
		self operator--(int)
		{
			self s(*this);
			_node = _node->_prev;
			return s;
		}
		
		node* _node;
	};
	//list链表的结构
	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;
//迭代器接口
		iterator begin()
		{
			//隐式类型转化成迭代器。
			return _head->_next;
			//return iterator(_head->_next);
			//iterator it(_head->next)  return it;
		}
		
		iterator end()
		{
			//最后一个节点的下一个位置,即_head.
			return _head;
		}
		const_iterator begin()const
		{
			return _head->_next;
		}
		const_iterator end()const
		{
			return _head;
		}
//构造接口
		list()
		{
			//会调用节点的构造函数初始化
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
			
		}
		//迭代器拷贝构造
     template<class inputiterator>
	 list(inputiterator first, inputiterator last)
	 {
		 _head = new node;
		 _head->_next = _head;
		 _head->_prev = _head;

		 while (first != last)
		 {
			 push_back(*first);
			 ++first;
		 }
		 
	 }
		//拷贝构造(深拷贝)
	    //忘记了,竟然还想想写赋值重载那样形参写成传值调用,你写传值就引发无穷递归了
		list(const list<T>& l)
		{
			//不初始化头节点,直接和别人交换,就会导致执行temp的析构函数(析构了随机值)时崩溃
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;

			list<T> temp(l.begin(),l.end());
			std::swap(_head, temp._head);
		}

		//赋值重载
		list<T>& operator=(list<T> l)
		{
		std::swap(_head,l.head);
		return *this;
		}
		
		~list()
		{
			clear();
			delete _head;
		}
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				it=erase(it);
			}
		}
		//尾插
		void push_back(const T& x)
		{
			//node* newnode = new node(x);
			//node* tail = _head->_prev;
			//tail->_next = newnode;
			//newnode->_prev = tail;

			//newnode->_next = _head;
			//_head->_prev = newnode;
			insert(end(),x);
		}
		void push_front(const T& x)
		{
			insert(begin(), x);
		}
		 // 1 2 3 4 
		void pop_back()
		{
			erase(--end());
		}
		void pop_front()
		{
			erase(begin());
		}
		//在pos之前插入
		iterator insert(iterator pos,const T& x)
		{
			node* newnode = new node(x);
			node* cur = pos._node;
			node* prev = cur->_prev;
		
			prev->_next = newnode;
			newnode->_prev = prev;

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

			//返回新插入的位置
			return newnode;
			
		}
		//删除
		iterator erase(iterator pos)
		{
			//不能删头
			assert(pos != end());
			node* cur = pos._node;
			node* prev = cur->_prev;
			node* next = cur->_next;
			prev->_next = next;
			next->_prev = prev;
			delete cur;

			return next;
		}
	private:
		node* _head;
	};
}
  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

楠c

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

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

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

打赏作者

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

抵扣说明:

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

余额充值