【C++】List类的简单模拟实现

一:大致结构类型的搭建(构造)

二:插入/删除元素的模拟实现

三:迭代器的三种模板参数

四:默认成员函数的模拟实现(拷贝,赋值,析构)

//////

什么是list:

1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向 其前一个元素和后一个元素。
3. listforward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高 效。
4. 与其他的序列式容器相比(arrayvectordeque)list通常在任意位置进行插入、移除元素的执行效率 更好。
5. 与其他序列式容器相比,listforward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list 的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间 开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这 可能是一个重要的因素.
/// ///

一:大致结构类型的搭建(构造)

1:由cplusplus知,list的底层是双向链表结构,所以我们需要建立起一个又一个的节点,那么如何实现呢?通过对STL库的源码的学习,大致了解到实现的过程,主要是通过封装,通过封装来封装节点,封装迭代器,同时通过模板使迭代器更加适配更多的类型;下面这张图大概是库中实现的主要结构:

 2:构造函数

由STL库知,list的构造函数就是构造出一个哨兵位的头结点,又因为我们将节点封装,所以对节点的构造需要在封装节点的类中进行构造,大致代码如下:

	//泛型模板:
	template <class T>
	struct list_node//封装节点
	{
		list_node<T>* _prev;
		list_node<T>* _next;
		T data;

		//结点构造函数,const T& x=T()是匿名构造,
		list_node(const T& x=T())
			:_prev(nullptr)
			, _next(nullptr)
			, data(x)
		{
		}
	};

	template <class T>
	class List
	{
		typedef list_node<T> node;//因为封装节点,list_node<T>这样写太繁琐,typedef重命名;
	public:

		//初始化,建立一个头结点,
		void empty_Init()//因为后面还有地方调用,所以写成一个可调用的函数;
		{
			_phead = new node;//构造结点,调用封装结点类中的构造函数
			_phead->_next = _phead;
			_phead->_prev = _phead;
		}
		List()//构造函数:
		{
			empty_Init();//初始化,建立一个头结点
		}
        
        //使用模板的构造函数
		template<class ListInputIterator>
		List(ListInputIterator first, ListInputIterator last)
		{
			empty_Init();//初始化建立头结点;
			while (first != last)
			{
				push_back(*first);//一直尾插;
				++first;
			}
		}



	private:
		node* _phead;//定义一个头结点
	};
/// ///

二:插入/删除元素的模拟实现

因为底层是双向链表结构,所以任意地方的插入与删除效率都非常高,具体实现代码如下所示:
        //插入删除是通过迭代器来插入/删除的;

        void insert(iterator pos, const T& x = T())//在POS指向的结点位置插入,匿名构造;
		{
			node* prev = pos._node->_prev;//记录迭代器所指向的结点的前一个;
			node* next = pos._node;//记录迭代器所指向的结点;
			node* newnode = new node(x);//创建一个新的结点,新结点在封装结点类中构造;
			
            //链接 迭代器指向结点的前一个结点    新结点   迭代器指向的结点
            prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = next;
			next->_prev = newnode;
		}


        //删除POS指向结点位置的结点
		iterator erase(iterator pos)//erase需要返回值,后面的析构需要通过返回值向后遍历,
		{
			assert(pos != _phead);//哨兵位的头结点不能删除;
			node* prev = pos._node->_prev;//记录迭代器所指向的结点的前一个;
			node* next = pos._node->_next;//记录迭代器所指向的结点的后一个;
			
            链接 迭代器指向结点的前一个结点  迭代器指向结点的后一个结点
            prev->_next = next;
			next->_prev = prev;
			return iterator(next);//返回删除后的迭代器所指向的结点;

		}

那么既然任意地方的插入/删除已经实现,那么头插,头删,尾插,尾删就迎刃而解了,可自己实现也可以直接复用insert/erase,具体代码如下所示:

         //尾插:
         void push_back(const T& x)
		{
            //自己链接:
			node* ptail = _phead->_prev;
			node* newnode = new node(x);
			ptail->_next = newnode;
			newnode->_prev = ptail;
			newnode->_next = _phead;
			_phead->_prev = newnode;
			
            //直接复用insert,
            //insert(_phead, x);
		}

        //头插:
		void push_front(const T& x)
		{
            //自己链接:
			node* next = _phead->_next;
			node* newnode = new node(x);
			_phead->_next = newnode;
			newnode->_prev = _phead;
			newnode->_next = next;
			next->_prev = newnode;

             //直接复用insert,
			//insert(_phead->_next, x);
		}

        //尾删:
		void pop_back()
		{
            //直接复用erase:
			erase(_phead->_prev);
		}

        //头删:
		void pop_fornt()
		{
            //直接复用erase:
			erase(_phead->_next);
		}

//////

三:迭代器的三种模板参数

1:正常的正向迭代器:

因为将迭代器封装,所以我们需要在封装的迭代器类中创建一个结点,方便遍历,同时通过对vector,与string的了解,迭代器遍历时需要运算符 “!=” ,“*” ,“++”;所以我们需要在封装的迭代器类中重载这些运算符;具体代码如下:

    //运用模板  
    template <class T>
	struct list_iterator
	{
		typedef list_node<T> node;//因为有封装结点,所以我们也要包含,同时重命名方便书写;
		typedef list_iterator<T> self;//重命名方便书写;
		node* _node;//定义一个结点,方便迭代器向后遍历;

		list_iterator(node* node)//迭代器构造函数:
			:_node(node)
		{

		}

		T& operator*()//重载解引用运算符,引用返回,返回的是迭代器指向结点的数据;
		{
			return _node->data;
		}

        //前置++:返回++后的结点(也就是下一个结点),
		self& operator++()//因为返回的是结点,所以返回值是迭代器;
		{
			_node = _node->_next;
			return *this;
		}

        //后置++:
		self operator++(int)//因为返回的是结点,所以返回值是迭代器;
		{
			self temp(*this);
			_node = _node->_next;
			return temp;
		}

        前置--:返回--后的结点(也就是上一个结点),
		self& operator--()//因为返回的是结点,所以返回值是迭代器;
		{
			_node = _node->_prev;
			return *this;
		}
        
        //后置--:
		self operator--(int)//因为返回的是结点,所以返回值是迭代器;
		{
			self temp(*this);
			_node = _node->_prev;
			return temp;
		}

		self& operator+(const T& x)
		{
			assert(x >= 0);
			int i = 0;
			while (i < x)
			{
				_node = _node->_next;
				++i;
			}
			return *this;
		}
        
     
		bool operator!=(const self& LL)
		{
			return _node != LL._node;
		}

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


template <class T>
	class List
	{

	public:

		typedef list_iterator<T> iterator;

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

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

///

2:const迭代器:

当我们没有重载const迭代器,那么当不是实例化对象调用迭代器时,那么将会权限放大的报错,但是这里却出现了以下的情况:

 没有重载const迭代器,却可以调用,还能正常运行,不会出现权限放大的报错,同时还可以随意更改迭代器所指向结点的值,这是不符合我们对迭代器的期望,而我们对迭代器的要求是遍历容器,而不是能随意更改容器的值,所以,这是不被允许的,所以我们需要重载一份const迭代器;

	//为了解决const 迭代器修改链表内容的问题,我们需要将运算符重载*给const化,防止被其修改;
	template <class T>
	struct const_list_iterator
	{
		typedef list_node<T> node;
		typedef const_list_iterator<T> self;
		node* _node;

		const_list_iterator(node* node)
			:_node(node)
		{

		}

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

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

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

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

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

		self& operator+(const T& x)
		{
			assert(x >= 0);
			int i = 0;
			while (i < x)
			{
				_node = _node->_next;
				++i;
			}
			return *this;
		}
		bool operator!=(const self& LL)
		{
			return _node != LL._node;
		}

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

当我们写完重载的const迭代器,我们会发现,const迭代器与普通迭代器的区别就是能不能修改,运算符 ‘*’ 是否被const修饰,同时因为写两份差不多的代码冗余,所以我们增加模板参数解决此类问题;即如下所示:


	//增加模板参数ref
    template <class T,class ref>
	struct list_iterator
	{
		typedef list_node<T> node;
		typedef list_iterator<T,ref> self;
		node* _node;

		list_iterator(node* node)
			:_node(node)
		{

		}
        
        //返回值是ref,当调用的是普通迭代器,那么返回值就是T&,
                     //当调用的是const迭代器,那么返回值就是const T&
		ref operator*()
		{
			return _node->data;
		}
		self& operator++()
		{
			_node = _node->_next;
			return *this;
		}

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

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

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

		self& operator+(const T& x)
		{
			assert(x >= 0);
			int i = 0;
			while (i < x)
			{
				_node = _node->_next;
				++i;
			}
			return *this;
		}
		bool operator!=(const self& LL)
		{
			return _node != LL._node;
		}

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

	template <class T>
	class List
	{
	public:
		typedef list_iterator<T,T&> iterator;
		typedef list_iterator<T,const T&> const_iterator;

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

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

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

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

	private:
		node* _phead;
	};
///
3:自定义类型的迭代器:
我们通过学习迭代器知道:
1:迭代器要么就是原生指针,
2:迭代器要么就是自定义类型对原生指针的封装,本质上是对指针的模拟;

当我们自定义一个类型时,那么普通迭代器和const迭代器就没办法实现我们需要的功能,就算能,看起来非常的不合理,不是我们印象中的自定义类型访问成员变量的样子,所以我们需要在重载一个“->”迭代器,使其可读性更高;

下面是不重载运算符“->”的写法,可以用,能成功运行,但是我们平常对结构体访问成员变量都是使用->,这里使用的"."访问就很不自然;

    //自定义类型,
    struct mm
	{
        //因为自定义类型的成员变量是公有,所以我们可以使用之前写的迭代器,
        //如果成员变量是私有,则需要重载流插入与流提取;
		int _m1;
		int _m2;

        //自定义类型初始化:
		mm(const int& a = 0, const int& b = 0)
			:_m1(a)
			, _m2(b)
		{
		}
	};

     
	void test_list3()
	{
		List<mm> l3;
		l3.push_back(mm(10, 20));
		l3.push_back(mm(30, 40));
		l3.push_back(mm(50, 60));
		cout << endl;
        
        //使用普通迭代器/const迭代器时也可以正常运行;
		List<mm>::iterator it = l3.begin();
		while (it != l3.end())
		{
            //迭代器本质上是对指针的模拟,此时迭代器指向链表中的结点,对其解引用找到其结点
            //又因为是自定义类型,所以一个一个的结点就是一个一个的自定义类型,(*it)=mm;
            //那么当访问成员变量时,(*it).找到成员变量;
			cout << (*it)._m1 << " " << (*it)._m2 << endl;
			++it;
		}
	}

所以,我们可以在迭代器封装里面重载一个运算符“->”,为了防止代码冗余,我们增加迭代器的模板参数,来实现运算符重载:
	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)
		{

		}

		ref operator*()
		{
			return _node->data;
		}

        //重载运算符‘->’,拿到迭代器指向的节点的地址;其他的运算符与普通/const迭代器一样
		ptr operator->()
		{
			return &_node->data;
		}

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

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

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

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

		self& operator+(const T& x)
		{
			assert(x >= 0);
			int i = 0;
			while (i < x)
			{
				_node = _node->_next;
				++i;
			}
			return *this;
		}
		bool operator!=(const self& LL)
		{
			return _node != LL._node;
		}

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

那么我们就不再会用“(*it).”访问成员变量,而使用我们较为熟悉的“->”来实现访问成员变量;

	struct mm
	{
		int _m1;
		int _m2;

		mm(const int& a = 0, const int& b = 0)
			:_m1(a)
			, _m2(b)
		{
		}
	};
	//自定义类型,结构体,如何访问结构体内成员
	void test_list3()
	{
		List<mm> l3;
		l3.push_back(mm(10, 20));
		l3.push_back(mm(30, 40));
		l3.push_back(mm(50, 60));
		cout << endl;
		List<mm>::iterator it = l3.begin();
		while (it != l3.end())
		{
			//it.operator->()找到迭代器指向的结点, ->_m1找到结点(自定义类型)的成员变量;
			//cout << it.operator->()->_m1 << " " << it.operator->()->_m2 << endl;

			cout << it->_m1 << " " << it->_m2 <<endl;//是编译器优化的结果,编译器将“operator->()->”两个->优化为一个->;

			//这两个本质差不多,一个是找到结点(自定义类型)的成员变量的地址然后解引用访问成员变量;
							//另一个是解引用找到结点(自定义类型),然后通过“.”来访问成员变量;
			//cout << *(&(it)->_m1) << " " << *(&(it)->_m2) << endl;
			//cout<<(*it)._m1<<" "<<(*it)._m2<<endl;
			++it;
		}
	}

//////

四:默认成员函数的模拟实现(拷贝,赋值,析构)

1:析构函数
链表释放空间需要一个一个遍历,一个一个释放,所以在实现析构函数之前,先实现一个清理结点函数,先将除了头结点以外的其他结点全部删除,具体代码如下:
		void clean()
		{
			iterator it = begin();
			while (it != end())
			{
				//it=erase(it);//因为调用erase时容易pos迭代器失效。所以需要通过返回值接收pos的位置;
				erase(it++);//因为我们重载了后置++,删除的是++之前的值,此时的it是it之后的值;
			}
		}

clern函数实现后,那么实现析构函数就非常简单,先调用clern函数删除不是头结点的结点,然后再将头结点释放掉,具体代码如下所示:

~List()
		{
			clern();
			delete _phead;
			_phead = nullptr;
		}

///

2:拷贝构造函数:
通过对string类与vector类的学习,我们可以有多种方式实现拷贝构造函数,在此我举两种:
1:一个一个遍历,然后赋值;
2:构建一个临时对象,调用模板构造,然后再调用STL库中的交换函数,
具体代码如下所示:
    //普通版本拷贝构造,一个一个拷贝;
		//List(const List<T> LL)
		//{
		//	empty_Init();//调用初始化函数,建立一个头结点;
		//	for (auto e : LL)
		//	{
		//		push_back(e);//一个一个尾插;
		//	}
		//}


		void swap(List<T>& temp)
		{
			std::swap(_phead, temp._phead);
		}
		//拷贝构造:通过库中swap函数交换:
		List(List<T>& LL)
		{
			empty_Init();
			List<T>temp(LL.begin(), LL.end());//模板构造
			swap(temp);
		}

///

3:赋值运算符重载函数:
赋值运算符重载也是调用STL库中的交换函数来完成, 但是切记不要传引用,传引用传值会将原对象中的数据改变,具体代码如下所示:
		List<T>& operator=(List<T> LL)//不能传引用,否则会改变原链表的值;
		{
			swap(LL);
			return *this;
		}

//////

至此,这是我对List类的一些接口的较浅理解,如果有不足之处,请各位大佬指正!

我将我所实现的简单list类放在下方,请各位批评指正!

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

namespace MKL
{
	//泛型模板:
	template <class T>
	struct list_node//封装节点
	{
		list_node<T>* _prev;
		list_node<T>* _next;
		T data;

		//构造节点函数,const T& x=T()是匿名构造,
		list_node(const T& x = T())
			:_prev(nullptr)
			, _next(nullptr)
			, data(x)
		{
		}
	};

	//包含普通迭代器/const迭代器/自定义类型迭代器
	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)
		{

		}

		//返回值是ref,当调用的是普通迭代器,那么返回值就是T&,
					 //当调用的是const迭代器,那么返回值就是const T&
		ref operator*()
		{
			return _node->data;
		}

		// //重载运算符‘->’,拿到迭代器指向的节点的地址;
		ptr operator->()
		{
			return &_node->data;
		}

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

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

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

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

		self& operator+(const T& x)
		{
			assert(x >= 0);
			int i = 0;
			while (i < x)
			{
				_node = _node->_next;
				++i;
			}
			return *this;
		}
		bool operator!=(const self& LL)
		{
			return _node != LL._node;
		}

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

	//普通版本迭代器
	//template <class T>
	//struct list_iterator
	//{
	//	typedef list_node<T> node;
	//	typedef list_iterator<T> self;
	//	node* _node;

	//	list_iterator(node* node)
	//		:_node(node)
	//	{

	//	}

	//	T& operator*()
	//	{
	//		return _node->data;
	//	}

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

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

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

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

	//	self& operator+(const T& x)
	//	{
	//		assert(x >= 0);
	//		int i = 0;
	//		while (i < x)
	//		{
	//			_node = _node->_next;
	//			++i;
	//		}
	//		return *this;
	//	}
	//	bool operator!=(const self& LL)
	//	{
	//		return _node != LL._node;
	//	}

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

	//为了解决const 迭代器修改链表内容的问题,我们需要将运算符重载*给const化,防止被其修改;
	//同时因为写两份差不多的代码冗余,所以我们增加模板参数解决此类问题
	//const版本迭代器:
	/*template <class T>
	struct const_list_iterator
	{
		typedef list_node<T> node;
		typedef const_list_iterator<T> self;
		node* _node;

		const_list_iterator(node* node)
			:_node(node)
		{

		}

		//运算符重载*const化
		const T& operator*()
		{
			return _node->data;
		}

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

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

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

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

		self& operator+(const T& x)
		{
			assert(x >= 0);
			int i = 0;
			while (i < x)
			{
				_node = _node->_next;
				++i;
			}
			return *this;
		}
		bool operator!=(const self& LL)
		{
			return _node != LL._node;
		}

		bool operator==(const self& LL)
		{
			return _node == LL._node;
		}
	};*/

	template <class T>
	class List
	{
		typedef list_node<T> node;//因为封装节点,list_node<T>这样写太繁琐,typedef重命名;
	public:

		/*typedef list_iterator<T> iterator;*/
		//typedef const_list_iterator<T> const_iterator;

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

		//初始化,建立一个头结点,
		void empty_Init()//因为后面还有地方调用,所以写成一个可调用的函数;
		{
			_phead = new node;
			_phead->_next = _phead;
			_phead->_prev = _phead;
		}
		List()//构造函数:
		{
			empty_Init();//初始化,建立一个头结点
		}

		//模板构造函数:
		template<class ListInputIterator>
		List(ListInputIterator first, ListInputIterator last)
		{
			empty_Init();
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		//普通版本拷贝构造,一个一个拷贝;
		//List(const List<T> LL)
		//{
		//	empty_Init();
		//	for (auto e : LL)
		//	{
		//		push_back(e);
		//	}
		//}

		void swap(List<T>& temp)
		{
			std::swap(_phead, temp._phead);
		}
		//拷贝构造:通过库中swap函数交换:
		List(List<T>& LL)
		{
			empty_Init();
			List<T>temp(LL.begin(), LL.end());
			swap(temp);
		}

		List<T>& operator=(List<T> LL)//不能传引用,否则会改变原链表的值;
		{
			swap(LL);
			return *this;
		}

		~List()
		{
			clern();
			delete _phead;
			_phead = nullptr;
		}

		//尾插:
		void push_back(const T& x)//1 2 3  4
		{
			//自己链接
			//node* ptail = _phead->_prev;
			//node* newnode = new node(x);
			//ptail->_next = newnode;
			//newnode->_prev = ptail;
			//newnode->_next = _phead;
			//_phead->_prev = newnode;

			//复用insert:
			insert(end(), x);
		}

		//头插:
		void push_front(const T& x)//1 2 3 4  5
		{
			//自己链接
			//node* next = _phead->_next;
			//node* newnode = new node(x);
			//_phead->_next = newnode;
			//newnode->_prev = _phead;
			//newnode->_next = next;
			//next->_prev = newnode;

			//复用insert:
			insert(begin(), x);
		}

		//尾删:
		void pop_back()
		{
			//复用erase:
			erase(_phead->_prev);
		}

		//头删,
		void pop_fornt()
		{
			//复用erase:
			erase(begin());
		}

		//任意地方插入:
		void insert(iterator pos, const T& x = T())
		{
			node* prev = pos._node->_prev;
			node* next = pos._node;
			node* newnode = new node(x);
			prev->_next = newnode;
			newnode->_prev = prev;
			newnode->_next = next;
			next->_prev = newnode;
		}

		//任意地方删除:
		iterator erase(iterator pos)
		{
			assert(pos != _phead);
			node* prev = pos._node->_prev;
			node* next = pos._node->_next;
			prev->_next = next;
			next->_prev = prev;
			return iterator(next);

		}

		//普通迭代器
		iterator begin()
		{
			return iterator(_phead->_next);
		}

		//普通迭代器
		iterator end()
		{
			return iterator(_phead);
		}

		//const迭代器
		const_iterator begin()const
		{
			return const_iterator(_phead->_next);
		}

		//const迭代器
		const_iterator end()const
		{
			return const_iterator(_phead);
		}

		void clean()
		{
			iterator it = begin();
			while (it != end())
			{
				//it=erase(it);//因为调用erase时容易pos迭代器失效。所以需要通过返回值接收pos的位置;
				erase(it++);//因为我们重载了后置++,删除的是++之前的值,此时的it是it之后的值;
			}
		}


	private:
		node* _phead;
	};
	//构建链表基本结构与普通迭代器,以及任意地方插入与删除:
	void test_List1()
	{
		List<int> l1;
		l1.push_back(1);
		l1.push_back(2);
		l1.push_back(3);
		l1.push_back(4);
		l1.push_back(5);
		List<int>::iterator it = l1.begin();
		while (it != l1.end())
		{
			cout << *(it) << " ";
			++it;
		}
		cout << endl;
		l1.push_front(10);
		l1.insert(l1.begin() + 2, 20);
		l1.erase(l1.begin() + 4);
		for (auto e : l1)
		{
			cout << e << " ";
		}
		cout << endl;

	}

	
	void my_print(List<int>& LL)
	{
		List<int>::iterator it = LL.begin();
		while (it != LL.end())
		{
			*(it) *= 2;
			cout << *(it) << " ";
			++it;
		}
		cout << endl;
	}
	//const迭代器:
	void test_list2()
	{
		List<int> l2;
		l2.push_back(10);
		l2.push_back(20);
		l2.push_back(30);
		l2.push_back(40);
		l2.push_back(50);
		for (auto e : l2)
		{
			cout << e << " ";
		}
		cout << endl;
		my_print(l2);
	}

	struct mm
	{
		int _m1;
		int _m2;

		mm(const int& a = 0, const int& b = 0)
			:_m1(a)
			, _m2(b)
		{
		}
	};
	//自定义类型,结构体,如何访问结构体内成员
	void test_list3()
	{
		List<mm> l3;
		l3.push_back(mm(10, 20));
		l3.push_back(mm(30, 40));
		l3.push_back(mm(50, 60));
		cout << endl;
		List<mm>::iterator it = l3.begin();
		while (it != l3.end())
		{
			//it.operator->()找到迭代器指向的结点, ->_m1找到结点(自定义类型)的成员变量;
			//cout << it.operator->()->_m1 << " " << it.operator->()->_m2 << endl;

			cout << it->_m1 << " " << it->_m2 <<endl;//是编译器优化的结果,编译器将“operator->()->”两个->优化为一个->;

			//这两个本质差不多,一个是找到结点(自定义类型)的成员变量的地址然后解引用访问成员变量;
							//另一个是解引用找到结点(自定义类型),然后通过“.”来访问成员变量;
			//cout << *(&(it)->_m1) << " " << *(&(it)->_m2) << endl;
			//cout<<(*it)._m1<<" "<<(*it)._m2<<endl;
			++it;
		}
	}

	//拷贝/赋值/析构函数
	void test_list4()
	{
		List<int> l4;
		l4.push_back(10);
		l4.push_back(20);
		l4.push_back(30);
		l4.push_back(40);
		l4.push_back(50);
		for (auto e : l4)
		{
			cout << e << " ";
		}
		cout << endl;

		//拷贝构造:
		List<int> l5(l4);
		for (auto e : l5)
		{
			cout << e << " ";
		}
		cout << endl;
		for (auto e : l4)
		{
			cout << e << " ";
		}
		cout << endl;

		//调用赋值运算符:
		List<int> l6;
		l6 = l4;
		for (auto e : l6)
		{
			cout << e << " ";
		}
		cout << endl;
		for (auto e : l4)
		{
			cout << e << " ";
		}
		cout << endl;
	}
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值