C++(List的实现)

1. list和vector的比较

vector与list都是STL中非常重要的容器,但由于他们的底层不同使他们拥有不同的特性,这也造成如果想高效地使用他们就需要根据不同的场景选择使用;主要不同如下:


2. list的实现

2.1 链表的基本结构

list其实就是我们前面数据结构部分讲过的链表,那就说明他的增删查改实现难度并不大,但我们这里还要实现那肯定是有原因的,我们先来分析一下链表,和数据结构链表一样,我们要先定义一个结点的类,然后再定义链表,stl内的list是双向循环链表;按之前一样,我们先定义出结构,但注意一点的是,list里面的迭代器需要我们另外封装一下,因为stl内的list支持++和--,但又因为list在内存不是一块连续的空间,所以++和--操作我们就需要不同的方式进行实现;如下代码为链表的基本结构:

cpp 复制代码
namespace Tao
{
	template<class T>
	//定义一个结点的结构
	struct list_node
	{
		T _data;
		list_node<T>* _next;
		list_node<T>* _prev;

		//结点初始化
		list_node(const T& data = T())
			:_data(data)
			, _next(nullptr)
			, _prev(nullptr)
		{}
	};

	//封装一下迭代器
	template<class T>
	struct list_iterator
	{
		typedef list_node<T> Node;
		typedef list_iterator<T> Self;

		//成员变量
		Node* _node;

		//初始化链表对迭代器进行初始化
		list_iterator(Node* node)
			:_node(node)
		{}
	};

	//链表的结构
	template<class T>
	class list
	{
		typedef list_node<T> Node;
	public:
		typedef list_iterator<T> iterator;
		//链表初始化
		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;
		}
	private:
		Node* _head;
		size_t _size;
	};
}

这里可能会有疑问,为什么结点的结构和迭代器的结构是用struct 定义,那是因为我们接下来的代码是需要使用里面的成员变量,class和struct都可以定义类,只不过struct定义的类默认是公有,而class默认是私有必须写public:外界才可以访问,用处几乎是一样的。

还有一点就是链表构造函数初始化那里还要创建一个_head的原因是上面提到的这个是双向循环链表


2.2 begin和end的实现

begin的实现:begin不能直接返回头结点,因为头结点是哨兵位结点,并不是第一位元素,所以我们应该返回哨兵位的下一位结点才是头结点;这里有三种方法可以实现,第一种就是构造一个it迭代器存储的是_head->next地址,然后返回,第二种是匿名对象做返回值,第三种是只传参数单参构造支持隐式类型转换;如下代码所示:

cpp 复制代码
		iterator begin()
		{
			//1
		/*	iterator it(_head->_next);
			return it;*/
			//2
			//return iterator(_head->_next);
			return _head->_next;
		}

end的实现:end迭代器的指向是最后一个结点的下一个结点的位置,那么我们只需要直接返回他的哨兵位结点即可;如下代码所示:

cpp 复制代码
		iterator end()
		{
			return _head;
		}

2.3 insert和erase的实现

insert的实现:insert和前面数据结构的实现一样,而且insert的实现不会出现和前面vector的迭代器失效的那种情况因为链表不需要扩容等操作,但是呢,erase的实现是会出现迭代器失效的情况,所以为了保证代码接口统一,我们也让insert的返回值为迭代器(可以省略 返回值为void),插入实现如下图(图画的丑hhh)和代码可见:

cpp 复制代码
		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);
			newnode->_next = cur;
			cur->_prev = newnode;
			newnode->_prev = prev;
			prev->_next = newnode;
			++_size;
			return newnode;
		}

erase的实现:erase就是指定位置pos删除数据,但是删除数据了之后如果不对pos进行处理,那pos迭代器指向的位置还是被删除数据的位置,数据已经被删除了那就会造成迭代器失效(野迭代器)的情况,所以我们要让返回下一个结点的迭代器;如下代码所示:

cpp 复制代码
		iterator erase(iterator pos)
		{
			//不能把哨兵位结点删掉
			assert(pos != _head);

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

			prev->_next = next;
			next->_prev = prev;
			delete pos._node;
			--_size;
			return next;
		}

补充一下:链表里加个_size是为了方便计算链表大小,这样我们实现size()的时候就不用再遍历一遍链表,而直接返回_size即可;


2.4 重载"--""++"的实现

由于链表内的每个数据的位置都不是连续的空间,不支持随机访问(通过下表访问值),且无法通过++或者--找到下一个或者前一个位置的值,而vector可以,在上面的list和vector的比较中可见;但是库中的list的迭代器是可以同过++或者--找到下一个位置或者前一个位置,那我们就需要来看看stl源码,看一下他们是如何实现的,如下图可见:

我们不一定要全部都看懂,我们只需要知道大概思路就行,由++那里可见,(*node).next,我们不知道link_type还有(*node)是什么意思,但我们可以大概猜一下next,其实就是让node = node.next的意思(只是大概意思),而--则是让node = node.prev,让node走到node的前一个结点的位置;而他们的后置++和--则是通过重载复用来实现,那我们也可以让这个思路在2.5的内容中实现,我们先看运算符重载的代码实现,如下代码可见:

cpp 复制代码
	// const_iterator
	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--()
		{
			_node = _node->_prev;
			return *this;
		}
		
		bool operator!=(const Self& s) const
		{
			return _node != s._node;
		}

		bool operator==(const Self& s) const
		{
			return _node == s._node;
		}
	};

2.5 pop_back、pop_front、push_front、size、empty的实现

pop_back也就是尾删,尾删不能删掉哨兵位结点,又因为end指向哨兵位结点,那--end则是哨兵位的前一个结点,我们就只需要把哨兵位的前一个结点删掉就可以实现尾删;

pop_front就是头删,头删不能删掉哨兵位结点,我们要删掉哨兵位的下一个结点也就是next指针指向的结点即begin()的返回值,那我们知道这个思路之后其实我们就可以实现了,但由上说道我们可以通过函数的复用实现;pop_back就是erase掉(--end())的结点即可,pop_front则是直接删掉begin()结点即可;如下代码可见:

cpp 复制代码
	template<class T>
	class list
	{
		typedef list_node<T> Node;
	public:
		typedef list_iterator<T> iterator;

		list()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;
		}
		iterator begin()
		{
			//1
		/*	iterator it(_head->_next);
			return it;*/
			//2
			//return iterator(_head->_next);
			return _head->_next;
		}

		iterator end()
		{
			return _head;
		}

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

			Node* newnode = new Node(x);

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

			++_size;
		}

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

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

			prev->_next = next;
			next->_prev = prev;
			delete pos._node;

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

			++_size;*/

			insert(end(), x);
		}

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



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

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


		size_t size() const
		{
			return _size;
		}

		bool empty() const
		{
			return _size == 0;
		}
	private:
		Node* _head;
		size_t _size;
	};

push_front、empty、size这些和前面几乎类似,就不过多讲述了;


2.6 拷贝构造的实现

拷贝构造的操作是已经创建了一个链表list1,里面存放了3、2、2、1的数据,然后我们想通过list1这个已经实例化的对象再实例化出一个新的对象list2-> list<int> list2(s1);那实现这个的话,我们首先要初始化,因为不先初始化的话没有哨兵位结点,然后我们再把list1的数据拷贝放到list2即可,拷贝构造和构造函数初始化都要初始化创建哨兵位结点,那么我们可以选择另外分装一个函数为EmptyInit函数,然后构造函数调用那个EmptyInit用于初始化即可;

cpp 复制代码
	template<class T>
	class list
	{
		typedef list_node<T> Node;
	public:
		typedef list_iterator<T> iterator;

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

			for (auto& e : lt)
			{
				push_back(e);
			}
		}
	private:
		Node* _head;
		size_t _size;
	};

2.7 clear()、析构函数、swap()和赋值运算符重载的实现

由下图可见,clear()是清空链表内的所有元素,那清空的操作我们就可以使用erase来实现,而clear在析构函数内也用得上,所以等会实现析构函数的时候直接就使用,使用完后就清理了链表内的元素,但还有一个哨兵位结点没有清理,那我们在实现析构函数的时候还要清理一下哨兵位结点;如下代码所示:

cpp 复制代码
		void clear()
		{
			auto it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}

由前面可知,赋值运算符的重载是可以通过类内的swap实现的,但我们需要先实现一个类内的swap先;至于为什么不直接调用库里的swap,stl还要自身实现一个swap的具体原因就不在这讲了,前面实现vector的时候讲过了,简单点说就是更高效,性能更好;如下为实现代码:

cpp 复制代码
		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}

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

		}

2.8 const_iterator的实现和优化

在前面实现vector的时候就可以知道,迭代器还有个const版即指向的内容不可被修改,要想实现其实很简单,就跟刚开始那样分装多一个iterator就行,只不过他们的值无法被修改;如下代码可见:

cpp 复制代码
	template<class T>
	struct list_const_iterator
	{
		typedef list_node<T> Node;
		typedef list_const_iterator<T> Self;
		Node* _node;

		list_const_iterator(Node* node)
			:_node(node)
		{}
		Self& operator++()
		{
			_node = _node->_next;
			return *this;
		}

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

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

			return tmp;
		}

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

			return tmp;
		}

		bool operator!=(const Self& s) const
		{
			return _node != s._node;
		}

		bool operator==(const Self& s) const
		{
			return _node == s._node;
		}
	};

但我们观察一下发现,const_iterator和iterator的实现真的超级相似,当出现这种情况的时候我们就可以通过修改模板参数来对其进行优化,我们就对iterator那个类进行优化,在封装迭代器的时候的模板设置加多几个参数,当传入const T& 或者 T&的时候就可以进行分辨;如下代码所示:

cpp 复制代码
	//封装一下结点迭代器
	//因为原本结点的指针无法通过++或者--来达到遍历的效果;
	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)
		{}

		//解引用返回一个值并且可以被修改
		//T& operator*()
		//{
		//	return _node->_data;
		//}
		//这里改成Ref的原因是可能会传入const T& 也有可能会传入T& ,
		//写成模板Ref的话就不担心说还要写多一个const_iterator了
		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 tmp(*this);
			_node = _node->_prev;
			return tmp;
		}
		bool operator!=(const Self& s)const
		{
			return _node != s._node;
		}

		bool operator==(const Self& s)const
		{
			return _node == s._node;
		}
	};

而且还要记得,在下面链表的类里面typedef多一个const_iterator即可;如下可见:

cpp 复制代码
	//链表
	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;

		//begin()是 我们要返回指向头的迭代器,那我们就需要用
		//那我们就需要构造第一个结点的指针 
		//构造第一个结点的迭代器可以用第一个节点的指针实现  
		//那我们要构造某个结点的迭代器就需要一个构造函数 跳转到35行
		iterator begin()
		{

			//第一种
			//return _head->next;

			//第二种匿名对象
			return iterator(_head->_next);

			第三种
			//iterator it(_head->next);
			//return it;
		}

		const_iterator begin()const
		{
			//单参数构造会进行隐式类型转换
			return _head->_next;
		}

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

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


	private:
		//哨兵位结点
		Node* _head;
		size_t _size;
	};

2.9 initializer_list的实现

initializer_list是C++11提供的新类型,在stl的list里面可以使用这个类型来进行初始化,我们先上个代码看一下会实现什么样的操作;如下代码所示:

cpp 复制代码
#include<iostream>
#include<iostream>
#include<algorithm>
#include"list.h"
#include<list>
using namespace std;

int main()
{
	list<int>lt{ 1,2,3,4,5,6 };
	Tao::print_container(lt);
	return 0;
}

👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇

调试结果为:

这里打印两次是因为我在前面list实现的域内实现了个print_container打印两次list内的元素;我们看,这个和我们之前见过的初始化不一样,他是用一个花括号括起来进行实例化,一般来说当我们看到右花括号括进行初始化的我们都可以认为是调用了initializer_list来初始化;

下面我们就来讲一下这个initializer_list的底层;首先我们看文献中给了这么一串代码,那我们拿到编译器编译一下看看;

如上图可见他的类型为initializer_list<int>,那我们就直接取这个类型出来进行测试一下;如下图:

👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇

调试结果为:

由此可见initializer_list的大小为8个字节,然后经过调试,发现他的底层是开了两个指针一个指针指向first一个指针指向last,这就和数组很类似了,就像是开了一个新的数组来存储这些值;

OK,在我们大概知道了initializer_list的底层后我们就可以在list内进行实现了;

我们再list里面实现的思路是:因为initializer list支持迭代器,支持迭代器那就支持范围for,因为范围for的底层是迭代器,那么我们就可以用范围for进行遍历然后插入数据进list即可;如下代码所示:

cpp 复制代码
		list(initializer_list<T> il1)
		{
			EmptyInit();
			for (auto& e : il1)
			{
				push_back(e);
			}
		}

实现完后我们就可以直接调用我们域内的使用了;如下代码为演示:

cpp 复制代码
#include<iostream>
#include<iostream>
#include<algorithm>
#include"list.h"
#include<list>
using namespace std;

int main()
{

	Tao::list<int> it = { 1,2,3,4,5,6999 };
	Tao::print_container(it);
	return 0;
}

👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇

调试结果为:

最后这里提一点就是单参数构造支持隐式类型转换;如下代码可见:

cpp 复制代码
	void func(const list<int>& it)
	{
		print_container(it);
	}
	void testlist7()
	{
		list<int> lt0({ 1,2,3,4,5,6666 });
		func(lt0);
		//单参构造函数支持隐式类型转换
		func({ 6,6,6,6,6,6 });
    }

#include<iostream>
#include<iostream>
#include<algorithm>
#include"list.h"
#include<list>
using namespace std;


int main()
{
	Tao::testlist7();
	return 0;
}

👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇 👇

调试结果为:;


3. list实现的所有代码

list.h

cpp 复制代码
#pragma once
#include<assert.h>
#include<iostream>

using namespace std;


namespace Tao
{
	//创建一个结点类型,因为结点要经常调用他的
	//next prev等等,所以可以放在struct里面
	//因为struct都是公有
	template<class T>
	struct list_node
	{
		T _data;
		list_node<T>* _prev;
		list_node<T>* _next;

		//结点的初始化
		list_node(const T& data = T())
			:_data(data)
			,_prev(nullptr)
			,_next(nullptr)
		{}
	};

	//封装一下结点迭代器
	//因为原本结点的指针无法通过++或者--来达到遍历的效果;
	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)
		{}


		//解引用返回一个值并且可以被修改
		//T& operator*()
		//{
		//	return _node->_data;
		//}
		//这里改成Ref的原因是可能会传入const T& 也有可能会传入T& ,
		//写成模板Ref的话就不担心说还要写多一个const_iterator了
		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 tmp(*this);
			_node = _node->_prev;
			return tmp;
		}

		//如果说我们上面的iterator的模板没有传入三个参数的话就需要另外创建一个const_iterator
		/*template<class T>
struct list_const_iterator
{
	typedef list_node<T> Node;
	typedef list_const_iterator<T> Self;
	Node* _node;

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

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

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

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

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

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

		return tmp;
	}

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

		return tmp;
	}

	bool operator!=(const Self& s) const
	{
		return _node != s._node;
	}

	bool operator==(const Self& s) const
	{
		return _node == s._node;
	}
};*/

		bool operator!=(const Self& s)const
		{
			return _node != s._node;
		}

		bool operator==(const Self& s)const
		{
			return _node == s._node;
		}
	};

	//链表
	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;
		//构造函数对链表进行初始化
		void EmptyInit()
		{
			_head = new Node(T());
			_head->_next = _head;
			_head->_prev = _head;
			_size = 0;
		}

		list()
		{
			EmptyInit();
		}
		list(const list<T>& lt)
		{
			EmptyInit();

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

		list(initializer_list<T> il1)
		{
			EmptyInit();
			for (auto& e : il1)
			{
				push_back(e);
			}
		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}

		list<T>& operator=(list<T> lt)
		{
			swap(lt);
			return *this;
		}

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

		}

		void clear()
		{
			auto it = begin();
			while (it != end())
			{
				it = erase(it);
			}
		}


		//begin()是 我们要返回指向头的迭代器,那我们就需要用
		//那我们就需要构造第一个结点的指针 
		//构造第一个结点的迭代器可以用第一个节点的指针实现  
		//那我们要构造某个结点的迭代器就需要一个构造函数 跳转到35行
		iterator begin()
		{

			//第一种
			//return _head->next;

			//第二种匿名对象
			return iterator(_head->_next);

			第三种
			//iterator it(_head->next);
			//return it;
		}

		const_iterator begin()const
		{
			//单参数构造会进行隐式类型转换
			return _head->_next;
		}

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

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

		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;
			//记录错误会显示在形参表中不匹配
			//++size
			++_size;
		}

		iterator insert(iterator pos, const T& x)
		{
			Node* cur = pos._node;
			Node* prev = cur->_prev;
			Node* newnode = new Node(x);
			newnode->_next = cur;
			cur->_prev = newnode;
			newnode->_prev = prev;
			prev->_next = newnode;
			++_size;
			return newnode;
		}

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

		iterator erase(iterator pos)
		{
			//不能把哨兵位结点删掉
			assert(pos != _head);

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

			prev->_next = next;
			next->_prev = prev;
			delete pos._node;
			--_size;
			return next;
		}

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

		size_t size()
		{
			return _size;
		}
		bool empty()const
		{
			return _size == 0;
		}

	private:
		//哨兵位结点
		Node* _head;
		size_t _size;
	};

	struct AA
	{
		int _a1 = 1;
		int _a2 = 1;
	};
	//void testlist01()
	//{
	//	list<int> lt;
	//	lt.push_back(1);
	//	lt.push_back(2);
	//	lt.push_back(3);
	//	lt.push_back(4);
	//	lt.push_back(5);
	//	lt.push_back(6);

	//	list<int>::iterator it1 = lt.begin();
	//	while (it1 != lt.end())
	//	{
	//		cout << *it1 << " ";
	//		++it1;
	//	}
	//	cout << endl;
	//}

	//void testlist02()
	//{
	//	list<int> lt;
	//	lt.push_back(1);
	//	lt.push_back(2);
	//	lt.push_back(3);
	//	lt.push_back(4);
	//	lt.push_back(5);
	//	lt.push_back(6);

	//	int x;
	//	cin >> x;
	//	lt.insert(lt.begin(), x);
	//	for (auto e : lt)
	//	{
	//		cout << e << " ";

	//	}
	//}

	template<class Container>
	void print_container(const Container& con)
	{
		// const iterator -> 迭代器本身不能修改
		// const_iterator -> 指向内容不能修改
		typename Container::const_iterator it = con.begin();
		//auto it = con.begin();
		while (it != con.end())
		{
			//*it += 10;

			cout << *it << " ";
			++it;
		}
		cout << endl;

		for (auto e : con)
		{
			cout << e << " ";
		}
		cout << endl;
	}

	void test_list2()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);

		// insert以后迭代器不失效
		list<int>::iterator it = lt.begin();
		lt.insert(it, 10);
		*it += 100;

		print_container(lt);

		// erase以后迭代器失效
		// 删除所有的偶数
		it = lt.begin();
		while (it != lt.end())
		{
			if (*it % 2 == 0)
			{
				it = lt.erase(it);
			}
			else
			{
				++it;
			}
		}

		print_container(lt);
	}

	void testlist03()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);
		lt.push_back(5);
		lt.push_back(6);
		lt.erase(lt.begin());
		lt.pop_back();
		lt.pop_front();

		list<int>::iterator it1 = lt.begin();
		while (it1 != lt.end())
		{
			cout << *it1 << " ";
			++it1;
		}
	}
	
	void testlist04()
	{
		list<int> lt1;
		lt1.push_back(1);
		lt1.push_back(2);
		lt1.push_back(3);
		lt1.push_back(4);
		lt1.push_back(5);
		lt1.push_back(6);

		list<int> lt2(lt1);

		auto it = lt2.begin();
		while (it != lt2.end())
		{
			cout << *it << " ";
			it++;
		}
	}

	void test_list1()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);

		list<int>::iterator it = lt.begin();
		while (it != lt.end())
		{
			*it += 10;

			cout << *it << " ";
			++it;
		}
		cout << endl;

		for (auto e : lt)
		{
			cout << e << " ";
		}
		cout << endl;
		print_container(lt);

		list<AA> lta;
		lta.push_back(AA());
		lta.push_back(AA());
		lta.push_back(AA());
		lta.push_back(AA());
		list<AA>::iterator ita = lta.begin();
		while (ita != lta.end())
		{
			//cout << (*ita)._a1 << ":" << (*ita)._a2 << endl;
			// 特殊处理,本来应该是两个->才合理,为了可读性,省略了一个->
			cout << ita->_a1 << ":" << ita->_a2 << endl;
			cout << ita.operator->()->_a1 << ":" << ita.operator->()->_a2 << endl;

			++ita;
		}
		cout << endl;
	}

	void test_list6()
	{
		list<int> lt;
		lt.push_back(1);
		lt.push_back(2);
		lt.push_back(3);
		lt.push_back(4);

		// insert以后迭代器不失效
		list<int>::iterator it = lt.begin();
		lt.insert(it, 10);
		*it += 100;

		print_container(lt);

		// erase以后迭代器失效
		// 删除所有的偶数
		it = lt.begin();
		while (it != lt.end())
		{
			if (*it % 2 == 0)
			{
				it = lt.erase(it);
			}
			else
			{
				++it;
			}
		}
		print_container(lt);
	}

	void func(const list<int>& it)
	{
		print_container(it);
	}
	void testlist7()
	{
		list<int> lt0({ 1,2,3,4,5,6666 });
		func(lt0);
		//单参构造函数支持隐式类型转换
		func({ 6,6,6,6,6,6 });

	}

}

END!

相关推荐
小技与小术2 分钟前
数据结构之树与二叉树
开发语言·数据结构·python
Beau_Will3 分钟前
数据结构-树状数组专题(1)
数据结构·c++·算法
hccee24 分钟前
C# IO文件操作
开发语言·c#
hummhumm29 分钟前
第 25 章 - Golang 项目结构
java·开发语言·前端·后端·python·elasticsearch·golang
hunandede35 分钟前
av_image_get_buffer_size 和 av_image_fill_arrays
c++
J老熊39 分钟前
JavaFX:简介、使用场景、常见问题及对比其他框架分析
java·开发语言·后端·面试·系统架构·软件工程
zmd-zk1 小时前
flink学习(2)——wordcount案例
大数据·开发语言·学习·flink
好奇的菜鸟1 小时前
Go语言中的引用类型:指针与传递机制
开发语言·后端·golang
Alive~o.01 小时前
Go语言进阶&依赖管理
开发语言·后端·golang
花海少爷1 小时前
第十章 JavaScript的应用课后习题
开发语言·javascript·ecmascript