List和list迭代器的模拟实现

list的使用

我们list表示带头双向循环链表。
我们再找我们的list中每个数据的位置,就不可以再用我们的下标+[]了。
我们就必须用迭代器。

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())
	{
		cout << *it << " ";
		it++;
	}cout << endl;
	list<int>::iterator found = find(lt.begin(), lt.end(), 3);
	lt.insert(found, 20);
	for (auto n : lt)
	{
		cout << n << " ";
	}
	cout << endl;
}
int main()
{
	test_list1();
	return 0;
}

sort的使用

我们库里面有sort,可以给我们的容器使用,那为什么我们链表不用我们库里的sort,而自己有自己的sort
是因为他用不了库里面的sort

理论而言,模板的语法上可以传任意类型的参数,但是内部使用迭代器是由要求的,名字会暗示你要传随机迭代器

在这里插入图片描述
在这里插入图片描述
第一个是库里的迭代器,是随机迭代器,下面是list中的迭代器类型,是双向迭代器,所以我们才不能用。

迭代器的分类
  1. 单向迭代器 (++)
  1. 双向迭代器(++ – )
  1. 随机迭代器(++ – + -)

其他的用法基本跟string和vector相似,我们大家举一反三就可以。

list的模拟实现

①list中链表的结点

就是我们链表的结点的内容,我们用一个类来表示

需要注意的就是我们用struct表示,因为他默认是公开的,我们之后都很一直要用它,设置成私有我们访问不了很麻烦

template<class T>
struct list_node
{
	T _val;
	list_node<T>* _next;
	list_node<T>* _prev;
};

②list的类中的成员变量和构造函数

我们list成员变量,已经我们怎么初始化

template<class T>
class list
{
public:
	//构造函数,进行初始化
	list()
	{
		//先创建一个哨兵位的头结点,
		//构成最简单的带头双向循环链表
		_head = new list_node;
		_head->_next = _head;
		_head->_prev = _head;
	}
private:
	//成员变量
	//链表的头结点。
	list_node* _head;
};

我上面的内容其实是有点错误的。
我们要想知道错误的点是什么,我们就明白类名,和类型的区别。

类名和类型
在我们没有模板参数的时候,我们类名就是表示类型。
但是有了类模板之后,我们就不可以这样认为了。
类名就是类名,表示类的名字。
如果我们想表示类型,我们就要加上模板参数,这样才叫类型。
所以我们再定义这个类的对象的时候,一定要写完整。
template<class T>
class list
{
public:
	list()
	{
		_head = new list_node<T>;
		_head->_next = _head;
		_head->_prev = _head;
	}
private:
	list_node<T>* _head;
};

但是我们为了美观、易读和我们以后操作方便,我们将这个类型重命名一下。

	template<class T>
	class list
	{
		typedef list_node<T> node;
	public:
		list()
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
		}
	private:
		node* _head;
	};

③尾插

跟我们的链表尾插的功能基本上是一样的。

void push_back(const T& x)
{
	//创建一个新结点,我们再new的时候走的构造函数。
	// 我们可以这样理解,node(x)是一个匿名对象。
	//所以我们将其 类list_node 写一个构造函数。
	node* newnode = new node(x);
	//找到尾
	node* tail = _head->_prev;
	//插入
	tail->_next = newnode;
	newnode->_prev = tail;
	newnode->_next = _head;
	_head->_prev = newnode;
}

我们将list_node的构造函数补上。

template<class T>
struct list_node
{
	T _val;
	list_node<T>* _next;
	list_node<T>* _prev;
	//默认构造 
	list_node(const T& x =T())
		:_next(nullptr)
		,_prev(nullptr)
		,_val(x)
	{}
};

insert 头插,尾插

对于list插入来说,并不会导致迭代器的失效

//pos位置之前
void 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;
}

当有了insert,list的头插和为尾插我们就可以很好的解决

//尾插
void push_back(const T& x)
{
	insert(end(), x);
}
//头插
void push_front(const T& x)
{
	insert(begin(), x);
}

erase 头删 尾删

他会导致迭代器失效,因为迭代器指向的位置没了

iterator erase(iterator pos)
{
	assert(pos != end());
	node* next = pos._node->_next;
	node* prev = pos._node->_prev;

	prev->_next = next;
	next->_prev = prev;
	delete pos._node;
	return iterator(next);
}

当有了erase,list的头删和为尾删我们就可以很好的解决

//头删
void pop_front(const T& x)
{
	erase(begin());
}
//尾删
void pop_back(const T& x)
{
	erase(--end());
}

⑥析构函数

我们在进行析构,我们先写一个函数将我们的结点清除。但是不清除头结点。

在删除的时候会迭代器失效,所以我们在erase加一个返回值解决迭代器失效的问题。返回下一个结点的位置

void clear()
{
	iterator it = begin();
	while (it != end())
	{
		//防止迭代器失效,增加返回值
		it = erase(it);
	}
}

析构就是将我们的头结点也删除掉

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

⑦迭代器区间的构造函数

就是用迭代器来构造我们的list

		//迭代器区间的构造函数
		template<class Iterator>
		list(Iterator first, Iterator last)
		{
			//我们没有头结点,就不能push_back
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
			while (first != last)
			{
				push_back(*first);
				first++;
			}
		}

⑧迭代器区间的拷贝构造

list(const list<T>& lt)
{
	_head = new node;
	_head->_next = _head;
	_head->_prev = _head;
	for (auto e : lt)
	{
		push_back(e);
	}
}

另一种拷贝构造的写法,比较现代的写法

void swap(list<T>& tmp)
{
	std::swap(_head, tmp._head);
}
list(const list<T>& lt)
{
	empty_init();
	list<T> tmp(lt.begin(), lt.end());
	swap(tmp);
}

⑨赋值重载

比较现代的写法

void swap(list<T>& tmp)
{
	std::swap(_head, tmp._head);
}
list<T>& operator = (list<T> lt)
{
	swap(lt);
	return *this;
}

④迭代器(最重要的)

对于我们字符串和数组的迭代器,我们可以认为一个指针,因为他们的空间连续,所以我们对指针加加,我们就可以将他们遍历一遍。

但是对于链表,它的每一个结点都是一个类。
我们想将它遍历一遍,它的空间也不连续,我们对指针加加也不能指向下一个空间。

大佬将迭代器设为一个类。我们通过将其封装能够让我们找到下一个结点的位置。下面我们就看看怎么封装的。

总结一下迭代器

1.迭代器要么是原生指针
2.迭代器要么就是自定义类型对原生指针的封装,模拟指针的行为。

list迭代器的封装

首先我们要知道我们迭代器要有什么,并且要实现什么功能。

功能:我们能够遍历链表,并且能够将链表的值返回出来。
实现:就可以用操作符重载,将++*重载一下。
++找到链表的下一个位置,
*返回链表的值。
!=,再遍历最后我们要停下来就要进行比较。

Ⅰ、迭代器的类的内容

template<class T>
//因为迭代器也是公开的所以我们用struct
struct __list_iterator
{
	//重命名我们如果想要修改的化就修改重命名的内容
	//而且看着很容易
	typedef __list_iterator<T> iterator;

	typedef list_node<T> node;
	//用结点的指针构造一个迭代器
	node* _node;

	//迭代器的构造函数
	__list_iterator(node* n)
		:_node(n)
	{}
	T& operator*()
	{
		return _node->_val;
	}
	iterator& operator++()
	{
		_node= _node->_next;
		return *this;  
	}
	//后置加加
	iterator operator++(int)
	{
		iterator tmp(_node);
		_node = _node->_next;
		return tmp;
	}
	//前置--
	iterator& operator--()
	{
		_node = _node->_prev;
		return *this;
	}
	//后置减减
	iterator operator--(int)
	{
		iterator tmp(_node);
		_node = _node->_prev;
		return tmp;
	}
	bool operator!=(const iterator& s)
	{
		return _node != s._node;
	}
	bool operator==(const iterator& s)
	{
		return _node == s._node;
	}
};

Ⅱ、在类中使用迭代器

这时,我们将迭代器的类写完了,我们就可以用迭代器。

template<class T>
class list
{
	typedef list_node<T> node;
	//将类的迭代器重命名一下,方便我们的使用
	typedef  __list_iterator<T> iterator;
	iterator begin()
	{
		//构建一个迭代器返回
		//第一种方法
		//iteartor it(_head->next);
		//return it;
		//第二种方法用匿名对象
		return iterator(_head->_next); 
	}
	iterator end()
	{
		return iterator(_head);
	}
public:
	list()
	{
		_head = new node;
		_head->_next = _head;
		_head->_prev = _head;
	}
private:
	node* _head;
};

我们封装的作用就体现出来了,让我们的操作跟前面vector一样。

Ⅲ、const迭代器

要有const迭代器,就再写一个const迭代器的类。如下面代码

template<class T>
//因为迭代器也是公开的所以我们用struct
struct __list_const_iterator
{
	//重命名我们如果想要修改的化就修改重命名的内容
	//而且看着很容易
	typedef __list_const_iterator<T> iterator;

	typedef list_node<T> node;
	//用结点的指针构造一个迭代器
	node* _node;

	//迭代器的构造函数
	__list_const_iterator(node* n)
		:_node(n)
	{}
	const T& operator*()
	{
		return _node->_val;
	}
	//前置加加
	iterator& operator++()
	{
		_node = _node->_next;
		return *this;
	}
	//后置加加
	iterator operator++(int)
	{
		iterator tmp(_node);
		_node = _node->_next;
		return tmp;
	}
	//前置--
	iterator& operator--()
	{
		_node = _node->_prev;
		return *this;
	}
	//后置减减
	iterator operator--(int)
	{
		iterator tmp(_node);
		_node = _node->_prev;
		return tmp;
	}
	bool operator!=(const iterator& s)
	{
		return _node != s._node;
	}
	bool operator==(const iterator& s)
	{
		return _node == s._node;
	}
};

在类中用const迭代器

	template<class T>
	class list
	{
		typedef list_node<T> node;
	public: 
		typedef  __list_const_iterator<T> const_iterator;
		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}
		const_iterator end()const
		{
			return const_iterator(_head);
		}

		list()
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
		}
	private:
		node* _head;
	};

我们要注意const修饰的对象是谁,有的修饰this指针,有的修饰迭代器,不一样,这些小细节,错一个都不行。

我们提一个问题,能不能在typedef中进行这样的操作,然后就不用写const迭代器呢。

	typedef  __list_iterator<T> iterator;
	typedef  const iterator const_iterator;
	//就是将我们的正向迭代器前加一个const表示我们的const迭代器,
	//既然我们后面没有用这种方法,肯定不行,至于为什们大家必须想明白
为什们不能用上述方法
首先我们必须要明白const修饰的对象是谁,修饰的对象,不一样我们的效果也不一样。
const int * p = &i,const修饰的是 *p,p指向的内容(*p)不可修改,但是p却可以修改。
我们的const迭代器想要的就是这种效果,迭代器可以++,但是迭代器解引用后指向的内容不可修改。
而我们的const迭代器就是将在解引用的时候传引用返回,变成const引用。
int * const p = &i,const修饰的对象是 p,p不可以修改,但是p指向的内容(*p)可以修改。
我们用这样的迭代器肯定是不对的,
typedef const __list_iterator<T> const_iterator;
这是我们typedef的想法,我们理解它不能就要理解const所要修饰对象。我们说迭代器就相当于指针。我们用const修饰这个迭代器的类,我们再用这个类的时候它的this指针被const修饰,而我们++的操作 _node = _node->_next;也就不可以++了。所以它可以类比成==> T *const。

但是我们发现,我们的正向迭代器和const正向迭代器,好像基本一样,就是引用返回的地方不一样,那为什么不合在一块呢?

Ⅳ、将两个迭代器合在一起

既然我们就几个地方不一样,那么我们再用模板的是再多一个参数,让编译器自己做类的实例化,我们相似的代码写一份就好了呀。

//增加一个模板参数
template<class T,class Ref>
struct __list_iterator
{

	typedef __list_iterator<T,Ref> iterator;

	typedef list_node<T> node;
	node* _node;

	__list_iterator(node* n)
		:_node(n)
	{}
	Ref operator*()
	{
		return _node->_val;
	}
	iterator& operator++()
	{
		_node = _node->_next;
		return *this;
	}
	iterator operator++(int)
	{
		iterator tmp(_node);
		_node = _node->_next;
		return tmp;
	}
	iterator& operator--()
	{
		_node = _node->_prev;
		return *this;
	}
	iterator  operator--(int)
	{
		iterator tmp(_node);
		_node = _node->_prev;
		return tmp;
	}
	bool operator!=(const iterator& s)
	{
		return _node != s._node;
	}
	bool operator==(const iterator& s)
	{
		return _node == s._node;
	}
};

那我们在list类中怎么用它呢。

template<class T >
class list
{
	typedef list_node<T> node;
public: 

	typedef  __list_iterator<T, T&> iterator;
	typedef  __list_iterator<T,const T&> const_iterator;

	iterator begin()
	{
		return iterator(_head->_next); 
	}
	iterator end()
	{
		return iterator(_head);
	}
	const_iterator begin() const
	{
		return const_iterator(_head->_next);
	}
	const_iterator end()const
	{
		return const_iterator(_head);
	}

	list()
	{
		_head = new node;
		_head->_next = _head;
		_head->_prev = _head;
	}
private:
	node* _head;
};

只要我们再用迭代器的时候我们传两个参数,就可以很容易的简化代码,但是效率并不会提升,只是编译器帮我们做了这些工作。

Ⅴ、迭代器的第三个参数

如果有一个自定义类型,我们想去用这个自定义类型当我们的链表,并且我们还想要访问我们的自定义类型里的数据,我们怎么办。

struct AA
{
	//默认构造
	AA(int a1 = 0, int a2 = 0)
		: _a1(a1)
		,_a2(a2)
	{}

	int _a1;
	int _a2;
};
void test_list2()
{
	list<AA> lt;
	lt.push_back(AA(1, 2));
	lt.push_back(AA());
	lt.push_back(AA(2, 1));
	list<AA>::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << (*it)._a1 << ":" << (*it)._a2 << endl;
		it++;
	}
	cout << endl;
}

想访问AA中的元素,我们上面给了一种做法就是解引用然后再找,因为我们的迭代器中重载了解引用操作符,所以我们可以用。

我们知道迭代器是一个指针,指针直接用->不就可以了吗?
所以只需要重载->就可以了。

迭代器中重载->

T* operator->()
{
	return &_node->_val;
}
list<AA>::iterator it = lt.begin();
while (it != lt.end())
{
	cout << it->_a1 << ":" << it->_a2 << endl;
	it++;
}

这样用了我们就可以使用->去访问自定义类型中的元素了。

为什们这样重载 ->?
将结点的自定义类型的地址返回,我们知道自定义类型的指针,对于结构体而言就可以使用 ->
但是我感觉大家会有疑问?
当重载完 ->it->表示AA类型的地址,不应该再用一个 ->才能够指向 _a1吗?
其实是为了可读性,省略了一个箭头。所以我们就可以调用了。
编译器会进行特殊处理

第三个参数

对于const迭代器的重载->,我们返回应该返回const T*.

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

所以我们可以在模板中再加一个参数,用来指这个。

Ⅵ迭代器最终形态

我们有三个模板参数就是为了搞const迭代器

//增加一个模板参数
template<class T,class Ref,class Ptr>
struct __list_iterator
{

	typedef __list_iterator<T,Ref,Ptr> iterator;

	typedef list_node<T> node;
	node* _node;

	__list_iterator(node* n)
		:_node(n)
	{}
	Ref operator*()
	{
		return _node->_val;
	}
	Ptr operator->()
	{
		return &_node->_val;
	}
	iterator& operator++()
	{
		_node = _node->_next;
		return *this;
	}
	iterator operator++(int)
	{
		iterator tmp(_node);
		_node = _node->_next;
		return tmp;
	}
	iterator& operator--()
	{
		_node = _node->_prev;
		return *this;
	}
	iterator  operator--(int)
	{
		iterator tmp(_node);
		_node = _node->_prev;
		return tmp;
	}
	bool operator!=(const iterator& s)
	{
		return _node != s._node;
	}
	bool operator==(const iterator& s)
	{
		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;
	//typedef  __list_const_iterator<T> const_iterator;
	iterator begin()
	{
		//构建一个迭代器返回
		//第一种方法
		//iteartor it(_head->next);
		//return it;
		//第二种方法用匿名对象
		return iterator(_head->_next); 
	}
	iterator end()
	{
		return iterator(_head);
	}
	const_iterator begin() const
	{
		return const_iterator(_head->_next);
	}
	const_iterator end()const
	{
		return const_iterator(_head);
	}

	list()
	{
		_head = new node;
		_head->_next = _head;
		_head->_prev = _head;
	}
private:
	node* _head;
};

list模拟实现的代码

list.h

#pragma once
#include<assert.h>
namespace tongtong
{
	template<class T>
	struct list_node
	{
		T _val;
		list_node<T>* _next;
		list_node<T>* _prev;

		list_node(const T& x =T())
			:_next(nullptr)
			,_prev(nullptr)
			,_val(x)
		{}
	};
	//template<class T>
	因为迭代器也是公开的所以我们用struct
	//struct __list_iterator
	//{
	//	//重命名我们如果想要修改的化就修改重命名的内容
	//	//而且看着很容易
	//	typedef __list_iterator<T> iterator;

	//	typedef list_node<T> node;
	//	//用结点的指针构造一个迭代器
	//	node* _node;

	//	//迭代器的构造函数
	//	__list_iterator(node* n)
	//		:_node(n)
	//	{}
	//	T& operator*()
	//	{
	//		return _node->_val;
	//	}
	//	//前置加加
	//	iterator& operator++()
	//	{
	//		_node = _node->_next;
	//		return *this;
	//	}
	//	//后置加加
	//	iterator operator++(int)
	//	{
	//		iterator tmp(_node);
	//		_node = _node->_next;
	//		return tmp;
	//	}
	//	//前置--
	//	iterator& operator--()
	//	{
	//		_node = _node->_prev;
	//		return *this;
	//	}
	//	//后置减减
	//	iterator operator--(int)
	//	{
	//		iterator tmp(_node);
	//		_node = _node->_prev;
	//		return tmp;
	//	}
	//	bool operator!=(const iterator& s)
	//	{
	//		return _node != s._node;
	//	}
	//	bool operator==(const iterator& s)
	//	{
	//		return _node == s._node;
	//	}
	//};
	//template<class T>
	//因为迭代器也是公开的所以我们用struct
	//struct __list_const_iterator
	//{
	//	//重命名我们如果想要修改的化就修改重命名的内容
	//	//而且看着很容易
	//	typedef __list_const_iterator<T> iterator;

	//	typedef list_node<T> node;
	//	//用结点的指针构造一个迭代器
	//	node* _node;

	//	//迭代器的构造函数
	//	__list_const_iterator(node* n)
	//		:_node(n)
	//	{}
	//	const T& operator*()
	//	{
	//		return _node->_val;
	//	}
	//	//前置加加
	//	iterator& operator++()
	//	{
	//		_node = _node->_next;
	//		return *this;
	//	}
	//	//后置加加
	//	iterator operator++(int)
	//	{
	//		iterator tmp(_node);
	//		_node = _node->_next;
	//		return tmp;
	//	}
	//	//前置--
	//	iterator& operator--()
	//	{
	//		_node = _node->_prev;
	//		return *this;
	//	}
	//	//后置减减
	//	iterator operator--(int)
	//	{
	//		iterator tmp(_node);
	//		_node = _node->_prev;
	//		return tmp;
	//	}
	//	bool operator!=(const iterator& s)
	//	{
	//		return _node != s._node;
	//	}
	//	bool operator==(const iterator& s)
	//	{
	//		return _node == s._node;
	//	}
	//};
	

	//增加一个模板参数
	template<class T,class Ref,class Ptr>
	struct __list_iterator
	{
	
		typedef __list_iterator<T,Ref,Ptr> iterator;

		typedef list_node<T> node;
		node* _node;

		__list_iterator(node* n)
			:_node(n)
		{}
		Ref operator*()
		{
			return _node->_val;
		}
		Ptr operator->()
		{
			return &_node->_val;
		}
		iterator& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		iterator operator++(int)
		{
			iterator tmp(_node);
			_node = _node->_next;
			return tmp;
		}
		iterator& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		iterator  operator--(int)
		{
			iterator tmp(_node);
			_node = _node->_prev;
			return tmp;
		}
		bool operator!=(const iterator& s)
		{
			return _node != s._node;
		}
		bool operator==(const iterator& s)
		{
			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;
		//typedef  __list_const_iterator<T> const_iterator;
		iterator begin()
		{
			//构建一个迭代器返回
			//第一种方法
			//iteartor it(_head->next);
			//return it;
			//第二种方法用匿名对象
			return iterator(_head->_next); 
		}
		iterator end()
		{
			return iterator(_head);
		}
		const_iterator begin() const
		{
			return const_iterator(_head->_next);
		}
		const_iterator end()const
		{
			return const_iterator(_head);
		}
		void empty_init()
		{
			_head = new node;
			_head->_next = _head;
			_head->_prev = _head;
		}
		list()
		{
			empty_init();
		}

		//迭代器区间的构造函数
		template<class Iterator>
		list(Iterator first, Iterator last)
		{
			//我们没有头结点,就不能push_back
			empty_init();
			while (first != last)
			{
				push_back(*first);
				first++;
			}
		}

		//list(const list<T>& lt)
		//{
		//	empty_init();
		//	for (auto e : lt)
		//	{
		//		push_back(e);
		//	}
		//}
		void swap(list<T>& tmp)
		{
			std::swap(_head, tmp._head);
		}
		list(const list<T>& lt)
		{
			empty_init();
			list<T> tmp(lt.begin(), lt.end());
			swap(tmp);
		}
		list<T>& operator = (list<T> lt)
		{
			swap(lt);
			return *this;
		}
		//尾插
		void push_back(const T& x)
		{
			//创建一个新结点,我们再new的时候走的构造函数。
			// 我们可以这样理解,node(x)是一个匿名对象。
			//所以我们将其 类list_node 写一个构造函数。
			//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);
		}


		//pos位置之前
		void 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;
		}
		iterator erase(iterator pos)
		{
			assert(pos != end());
			node* next = pos._node->_next;
			node* prev = pos._node->_prev;

			prev->_next = next;
			next->_prev = prev;
			delete pos._node;
			return iterator(next);
		}
		//头删
		void pop_front(const T& x)
		{
			erase(begin());
		}
		//尾删
		void pop_back(const T& x)
		{
			erase(--end());
		}

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}
		//清理结点,但是不清理头结点
		void clear()
		{
			iterator it = begin();
			while (it != end())
			{
				//防止迭代器失效,增加返回值
				it = erase(it);
			}
		}
	private:
		node* _head;
	};


	void print_list(const list<int>& lt)
	{
		list<int>::const_iterator cit = lt.begin();
		while (cit != lt.end())
		{
			/*(*cit)  *= 2;*/
			cout << *cit << " ";
			cit++;
		}
		cout << endl;
	}
	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())
		//{
		//	cout << *it << " ";
		//	++it;
		//}
		//cout << endl;
		//auto rit = lt.begin();
		//rit++;
		//lt.insert(rit, 20);
		//for (auto c : lt)
		//{
		//	cout << c << " ";
		//}
		//cout << endl;
		//print_list(lt);

		//lt.clear();
		for (auto c : lt)
		{
			cout << c << " ";
		}
		cout << endl;
		list<int> lt2 ;
		lt2 = lt;
		for (auto c : lt2)
		{
			cout << c << " ";
		}
		cout << endl;
		for (auto c : lt)
		{
			cout << c << " ";
		}
		cout << endl;
	}

	struct AA
	{
		//默认构造
		AA(int a1 = 0, int a2 = 0)
			: _a1(a1)
			,_a2(a2)
		{}

		int _a1;
		int _a2;
	};
	void test_list2()
	{
		list<AA> lt;
		lt.push_back(AA(1, 2));
		lt.push_back(AA());
		lt.push_back(AA(2, 1));
		list<AA>::iterator it = lt.begin();
		while (it != lt.end())
		{
			cout << it->_a1 << ":" << it->_a2 << endl;
			it++;
		}
		cout << endl;
	}
}
 	

test.cpp

#define _CRT_SECURE_NO_WARNINGS 1 
#include<iostream>
#include<list>

using namespace std;
#include"list.h"
int main()
{
	tongtong::test_list1();
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/3988.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MySQL基础-视图

文章目录MySQL基础-视图一、数据库对象二、视图概念三、视图的使用1、创建视图2、查看视图3、修改视图4、修改/删除视图四、总结MySQL基础-视图 一、数据库对象 对象描述表(TABLE)表是存储数据的逻辑单元&#xff0c;以行和列的形式存在&#xff0c;列就是字段&#xff0c;行…

STM32输出PWM波控制电机转速,红外循迹避障智能车+L298N的详细使用手册、接线方法及工作原理,有代码

智能循迹红外避障小车 本设计的完整的系统主要包括STM32单片机最小系统、L298n电机驱动&#xff0c;超声波 &#xff0c;舵机 &#xff0c;红外模块等。寻迹小车相信大家都已经耳熟能祥了。 我们在这里主要讲一下L298N驱动电机和单片机输出PWM控制电机转速。 本设计软件系统采…

这个时候了,你还不会不知道JavaMail API吧

一、概述 1.1 简述 JavaMail API 顾名思义&#xff0c;提供给开发者处理电子邮件相关的编程接口&#xff0c;它是Sun发布的用来处理email的API&#xff0c;其提供独立于平台且与协议无关的框架来构建邮件和消息传递应用。JavaMail API 提供了一组抽象类&#xff0c;用于定义组…

第十四届蓝桥杯三月真题刷题训练——第 24 天

目录 第 1 题&#xff1a;旋转 题目描述 输入描述 输出描述 输入输出样例 运行限制 代码&#xff1a; 思路&#xff1a; 第 2 题&#xff1a;附近最小_单调队列模板 代码&#xff1a; 第 3 题&#xff1a;扫地机器人_二分 题目描述 输入描述 输出描述 输入输出样…

【蓝桥杯】​蓝桥杯——每日四道编程题(两道真题+两道模拟)​| 第 二 天

专栏&#xff1a; 蓝桥杯——每日四道编程题&#xff08;两道真题两道模拟&#xff09; “蓝桥杯就要开始了&#xff0c;这些题刷到就是赚到” ₍ᐢ..ᐢ₎♡ 另一个专栏&#xff1a; 蓝桥杯——每日四道填空题&#xff08;两道真题两道模拟题&#xff09; 目录 第一道真题&…

Kafka 基础整理、 Springboot 简单整合

定义&#xff1a; Kafka 是一个分布式的基于发布/订阅默认的消息队列是一个开源的分布式事件流平台&#xff0c;被常用用于数据管道、流分析、数据集成、关键任务应用 消费模式&#xff1a; 点对点模式 (少用) 消费者主动拉取数据&#xff0c;消息收到后清除消息 发布/订阅模式…

【蓝桥杯嵌入式】第十四届蓝桥杯嵌入式[模拟赛2]客观题及详细题解

题1 解析 USART_CR1:控制寄存器1&#xff0c;其中的M位定义了数据字的长度&#xff0c;由软件对其设置和清零。USART_CR2:控制寄存器2。USART_BRR:波特率寄存器。USART_DR:数据寄存器。 (如果现场不记得&#xff0c;可以查阅芯片手册) 答案: A 题2 解析 在STM32微控制器中&a…

每日学术速递3.29

CV - 计算机视觉 | ML - 机器学习 | RL - 强化学习 | NLP 自然语言处理 Subjects: cs.CV 1.CC3D: Layout-Conditioned Generation of Compositional 3D Scenes 标题&#xff1a;CC3D&#xff1a;合成 3D 场景的布局条件生成 作者&#xff1a;Sherwin Bahmani, Jeong Joon …

五、MyBatis各种查询功能

MyBatis的各种查询功能 如果查询出的数据只有一条&#xff0c;可以通过 实体类对象接收List集合接收Map集合接收 如果查询出的数据有多条&#xff0c;一定不能用实体对象接收&#xff0c;会抛TooManyResultsException&#xff0c;可以通过 实体类类型的List集合接收Map类型的L…

学习系统编程No.11【重定向的本质】

引言&#xff1a; 北京时间&#xff1a;2023/3/27/7:05&#xff0c;哈哈哈&#xff0c;首先是开心&#xff0c;因为上篇博客热榜目前第15&#xff0c;让我初步掌握了上热榜的小妙招&#xff0c;不单单只是要日更&#xff0c;还有非常多的上榜小技巧&#xff0c;但是首先连续更…

【备战蓝桥杯】----01背包问题(动态规划)

&#x1f339;作者:云小逸 &#x1f4dd;个人主页:云小逸的主页 &#x1f4dd;Github:云小逸的Github &#x1f91f;motto:要敢于一个人默默的面对自己&#xff0c;强大自己才是核心。不要等到什么都没有了&#xff0c;才下定决心去做。种一颗树&#xff0c;最好的时间是十年前…

【数据结构】堆(堆的实现 堆向下调整算法 堆的创建 堆的插入 堆的删除 堆的代码实现 堆的应用)

文章目录堆的实现堆向下调整算法堆的创建堆的插入堆的删除堆的代码实现堆的应用堆的实现 堆是属于操作系统进程地址空间内存区域的划分。 我们下面实现数据结构中的堆。 堆是一个完全二叉树&#xff1a;分为小根堆和大根堆。 小根堆&#xff1a;任何一个节点的值都<孩子的…

YOLOv8原理解析:重新定义实时目标检测的速度和精度

文章目录0.前言1.YOLOv51.1 YOLOv5网络回顾1.2 YOLOv5网络结构图2.YOLOv82.1 YOLOv8概述2.2 YOLOv8整体结构图2.3 YOLOv8yaml 文件与 YOLOv5yaml 文件对比2.3.1 参数部分2.3.2 主干部分2.3.3 Neck部分2.3.4 Head部分2.4 正负样本分配策略2.4.1 静态分配策略和动态分配策略有什么…

【嵌入式烧录/刷写文件】-1.1-详解Motorola S-record(S19/SREC/mot/SX)格式文件

目录 1 什么是Motorola S-record 2 Motorola S-record的格式 2.1 Motorola S-record的结构 2.1.1 “Record type记录类型”的说明 2.1.2 “Record length记录长度”的说明 2.1.3 如何计算“Checksum校验和” 2.2 Record order记录顺序 2.3 Text line terminator文本行终…

【C语言】柔性数组

柔性数组1. 柔性数组介绍2. 柔性数组特点3. 用例3.1 代码一&#xff1a;3.2 代码二&#xff1a;4. 柔性数组优势&#xff1a;1. 柔性数组介绍 也许你从来没有听说过柔性数组&#xff08;flexible array&#xff09;这个概念&#xff0c;但是它确实是存在的。 C99 中&#xff0c…

#详细介绍!!! 线程池的拒绝策略(经典面试题)

本篇单独讲解线程池的拒绝策略&#xff0c;介绍了当线程池任务满了之后&#xff0c;线程池会以什么样的方式来响应添加进来的任务 目录 一&#xff1a;理解线程池拒绝策略的触发情况代码理解 二&#xff1a;线程池的四种常见的拒绝策略 1.ThreadPoolExecutor.AbortPolicy 2…

【Spring6】| GoF之代理模式(静态代理和动态代理)

目录 一&#xff1a;GoF之代理模式 1. 对代理模式的理解 2. 静态代理 3. 动态代理 3.1 JDK动态代理 3.2 CGLIB动态代理 一&#xff1a;GoF之代理模式 1. 对代理模式的理解 生活场景1&#xff1a;牛村的牛二看上了隔壁村小花&#xff0c;牛二不好意思直接找小花&#xff…

内网渗透之某后渗透利用【网络安全】

0x01 工具介绍 工具原理 Mimikatz 的主要原理是在 Windows 系统中用户登录后系统会将身份凭证存储于lsass.exe进程的内存当中&#xff0c;Mimikatz 通过注入lsass.exe进程读取进程内存&#xff0c;从中获取对应的明文密码。 常见问题 在 Windows Vista 系统之后不再存储 LM…

0203优先级下的调度问题_环_拓扑排序-有向图-数据结构和算法(Java)

1 概述 在和有向图相关的实际应用中&#xff0c;有向环特别的重要。在实际应用中&#xff0c;一般只会重点关注其中的一小部分&#xff0c;或者只想知道它们是否存在。 2 调度问题 一种应用广泛的模型是给定一组任务并安排它们的执行顺序&#xff0c;限制条件是这些任务的执…

机器学习:逻辑回归模型算法原理(附案例实战)

机器学习&#xff1a;逻辑回归模型算法原理 作者&#xff1a;i阿极 作者简介&#xff1a;Python领域新星作者、多项比赛获奖者&#xff1a;博主个人首页 &#x1f60a;&#x1f60a;&#x1f60a;如果觉得文章不错或能帮助到你学习&#xff0c;可以点赞&#x1f44d;收藏&#…
最新文章