【list的模拟实现】

news2024/9/21 11:11:43

list的模拟实现

小杨

list相关类要实现的接口

namespace yang
{
	// List的节点类
	template<class T>
	struct ListNode
	{
		ListNode(const T& val = T());

		ListNode<T>* _prev;
		ListNode<T>* _next;
		T _val;
	};
	//List的迭代器类
	template<class T, class Ref, class Ptr>
	class ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T, Ref, Ptr> Self;

		// Ref 和 Ptr 类型需要重定义下,实现反向迭代器时需要用到
	public:
		typedef Ref Ref;
		typedef Ptr Ptr;
	public:
		// 构造
		ListIterator(Node* node = nullptr);

		// 具有指针类似行为
		Ref operator*();
		Ptr operator->();

		// 迭代器支持移动
		Self& operator++();
		Self operator++(int);
		Self& operator--();
		Self operator--(int);
		// 迭代器支持比较
		bool operator!=(const Self& l)const;
		bool operator==(const Self& l)const;
		
		Node* _node;
	};

	//List的反向迭代器类
	template<class Iterator>
	class ReverseListIterator
	{
		// 注意:此处typename的作用是明确告诉编译器,Ref是Iterator类中的一个类型,而不是静态成员变量
		// 否则编译器编译时就不知道Ref是Iterator中的类型还是静态成员变量
		// 因为静态成员变量也是按照 类名::静态成员变量名 的方式访问的
	public:
		typedef typename Iterator::Ref Ref;
		typedef typename Iterator::Ptr Ptr;
		typedef ReverseListIterator<Iterator> Self;
	public:
		// 构造
		ReverseListIterator(Iterator it);

		// 具有指针类似行为
		Ref operator*();
		Ptr operator->();

		// 迭代器支持移动
		Self& operator++();
		Self operator++(int);
		Self& operator--();
		Self operator--(int);
		
		// 迭代器支持比较
		bool operator!=(const Self& l)const;
		bool operator==(const Self& l)const;

		Iterator _it;
	};
	//List类
	template<class T>
	class list
	{
		typedef ListNode<T> Node;

	public:
		// 正向迭代器
		typedef ListIterator<T, T&, T*> iterator;
		typedef ListIterator<T, const T&, const T*> const_iterator;

		// 反向迭代器
		typedef ReverseListIterator<iterator> reverse_iterator;
		typedef ReverseListIterator<const_iterator> const_reverse_iterator;
	public:
		// List的构造
		list();
		list(int n, const T& value = T());

		template <class Iterator>
		list(Iterator first, Iterator last);
		list(const list<T>& l);
		
		list<T>& operator=(list<T> l);
		
		~list();
		

		// List的迭代器
		iterator begin();
		iterator end();
		const_iterator begin()const;
		const_iterator end()const;
		reverse_iterator rbegin();
		reverse_iterator rend();
		const_reverse_iterator rbegin()const;
		const_reverse_iterator rend()const;
		
		// List的容量相关
		size_t size()const;
		bool empty()const;
		void resize(size_t newsize, const T& data = T());
		
		
		// List的元素访问操作
		// 注意:List不支持operator[]
		T& front();
		const T& front()const;
		T& back();
		const T& back()const;
		
		// List的插入和删除
		void push_back(const T& val);
		void pop_back();
		void push_front(const T& val);
		void pop_front();
		
		// 在pos位置前插入值为val的节点
		iterator insert(iterator pos, const T& val;

		// 删除pos位置的节点,返回该节点的下一个位置
		iterator erase(iterator pos);
	
		void clear();
		void swap(bite::list<T>& l);
		
	private:
		void CreateHead();
		
	private:
		Node* _head;
	};
}

List的节点类模拟实现

list在容器中是带头双向循环列表
他由一个一个的节点所构成,而这每一个节点,又由
**(前驱指针_prev、后继指针_next、当前节点值_val)**所构成。

那么因为list由一个一个节点构成,顾名思义,我们调用list的节点类,即能获得一个节点,因为该节点是我们的自定义类型,因此需要我们自己来进行写其的构造函数.
在这里插入图片描述
我们初始的节点,前驱节点和后继结点都置为空即可,节点所要存储的值传给_val即可。

	template<class T>
	struct ListNode
	{
		ListNode(const T& val = T());

		ListNode<T>* _prev;
		ListNode<T>* _next;
		T _val;
	};

//注意:这里参数给的是缺省参数,const T& val=T(),即给的缺省参数还是匿名对象,这个的好处就是,如果你存的是自定义类型的对象,那么就会调用自定义类型的默认构造,重点在后面,在C++中,可以认为int等内置类型也有构造。比如说 int k =int();

List的正向和反向迭代器

首先说明,迭代器的实现方式一共有两种

种类举例
原生态指针vector
原生态指针封装list

对于原生态指针来说,他们的底层往往是连续的,能通过指针增减以及解引用操作,就可以对所存储位置的数据进行一系列操作,因此vector当中的迭代器就是原生指针。

但是对于list来说,其各个节点在内存中的位置不确定,并不一定是连续的,因此必须借助迭代器封装,对其相应节点运算符进行重载,使得我们可以不用关心他们的底层,就可以实现像原生态指针类似的操作(对所存储位置进行一系列的操作)。例如,当你使用list当中的迭代器进行自增操作时,实际上执行了p = p->next语句,只是你不知道。

我们先看一下正向和反向迭代器的模版参数

迭代器类型迭代器模版参数
正向迭代器template<class T, class Ref, class Ptr>
反向迭代器template

是不是会有些许疑惑,这都是神魔奇怪名字?
让我们把目光放到全局,看到List类的模拟实现中
在这里插入图片描述
上图中可以看到,list的模拟实现中,typedef了4个迭代器类型,普通正向迭代器、const正向迭代器、普通反向迭代器、const反向迭代器.

目前了解到的:
我们发现,在正向迭代器类中Ref是引用类型,Ptr是指针类型。然后我们将正向迭代器typedef成了 iterator,将const正向迭代器typedef了const_iterator.而反向迭代器中的模版参数Iterator呢?
在这里插入图片描述
会发现,就是我们所想的那样,Iteartor就是刚才正向迭代器typedef后的iterator,也就是说反向迭代器的模版参数用的是正向迭代器,这么做的目的是是反向迭代器可以用正向迭代器的元素。
因为在正向迭代器中有以下代码

	// Ref 和 Ptr 类型需要重定义下,实现反向迭代器时需要用到
public:
	typedef Ref Ref;
	typedef Ptr Ptr;

这就意味反向迭代器可以用到正向迭代器中的Ref(引用)和Ptr指针。从而复用了代码。
同理,const反向迭代器和cosnt正向迭代器的关系也是如此。只不过多了const修饰,仅此而已。
在这里插入图片描述
总结:反向迭代器类依赖于正向迭代器类。反向迭代器类的实现通常会使用正向迭代器类作为其内部成员,通过正向迭代器类的操作来实现反向遍历。尽管反向迭代器依赖于正向迭代器,但它们是独立的类。反向迭代器类封装了正向迭代器类的实例,并通过重载操作符来提供反向遍历的功能。

构造函数

迭代器类实际上就是对节点指针进行了封装,因此其成员变量就只有一个即节点指针。
在这里插入图片描述

在这里插入图片描述
在方向迭代器类中,实例化了一个正向迭代器,其目的是用来反向迭代器类的实现。
并且反向迭代器的构造函数,应该是要接受一个正向迭代器作为参数,并赋值给成员变量,即我们刚才实例化的那个正向迭代器对象。并且传递这个正向迭代器参数,我们的反向迭代器就可以知道从哪个位置开始进行反向遍历。总的来说:这体现了封装的好处,就是我们通过封装一个正向迭代器实力,然后根据此我们可以利用正向迭代器的功能来实现反向遍历,即反向迭代器类不需要重新实现正向迭代器类的所有功能。

		//正向迭代器构造
		ListIterator(Node* node = nullptr)
			:_node(node);
		{

		}
	// 反向迭代器构造
	ReverseListIterator(Iterator it)
		:_it(it)
	{
	
	}
		
++运算符的重载

在这里插入图片描述

在这里插入图片描述

前置++

正向迭代器:将数据自增,然后返回自增后的数据。
反向迭代器:将数据自减,然后返回自减后的数据。

		//正向迭代器
		Self& operator++()
		{
			_node = _node->_next;
			return *this;
		}
		//反向迭代器
		Self& operator++()
		{
			--_it;
			return *this;
		}
后置++

正向迭代器:先保存当前节点指针的指向,然后让指针指向下一个,然后返回刚才保存的那个。
反向迭代器:先保存当前节点指针的指向,然后让指针指向前一个,然后返回刚才保存的那个。

		//正向迭代器
		Self operator++(int)
		{
			Self temp(*this);
			_node = _node->_next;
			return temp;
		}
		//反向迭代器
		Self operator++(int)
		{
			Self temp(*this);
			--_it;
			return temp;
		}
- -运算符的重载
前置–

正向迭代器:让节点先向前走一步,然后返回减减后的节点指针
反向迭代器:让节点先向后走一步,然后返回加加后的节点指针

		//正向迭代器
		Self& operator--()
		{
			_node = _node->_prev;
			return *this;
		}
		//反向迭代器
		Self& operator--()
		{
			++_it;
			return *this;
		}
后置–

正向迭代器:先保存当前节点,然后指向前一个节点,最后返回保存的节点
反向迭代器:先保存当前节点,然后指向后一个节点,最后返回保存的节点

		//正向迭代器
		Self operator--(int)
		{
			Self temp(*this);
			_node = _node->_prev;
			return *this;
		}
		//反向迭代器
		Self operator--(int)
		{
			Self temp(*this);
			++_it;
			return temp;
		}
==运算符重载和!=运算符重载

判断这两个迭代器是否是同一个位置的迭代器。

	//正向迭代器
	bool operator!=(const Self& l)const
	{
		return _node != l._node;
	}
	bool operator==(const Self& l)const
	{
		return _node == l._node;
	}

	//反向迭代器
	bool operator!=(const Self& l)const
	{
		return _it != l._it;
	}
	bool operator==(const Self& l)const
	{
		return _it == l._it;      
	}
*运算符重载

正向迭代器:返回当前节点的数据
反向迭代器:保存当前节点并拷贝一份副本,然后–副本,最后返回。原因:反向迭代器指向的位置实际上是正向迭代器的前一个位置。也就是说,如果正向迭代器_it指向位置i,那么反向迭代器应该返回位置i-1的元素。

		//正向迭代器
		Ref operator*()
		{
			return _node->_val;
		}
		//反向迭代器
		Ref operator*()
		{
			Iterator temp(_it);
			--temp;
			return *temp;
		}
->运算符的重载

正向迭代器:直接返回节点当中所存储数据的地址即可。
反向迭代器:复用operator*()并且取地址就可以。

		//正向迭代器
		Ptr operator->()
		{
			return &_node->_val;
		}
		//反向迭代器
		Ptr operator->()
		{
			return &(operator*());
		}

list的模拟实现

Member functions(成员函数)

在这里插入图片描述

默认构造函数

申请一个头节点,然后让前驱和后继指针都指向自己。

		list()
		{
			CreateHead();
		}
		private:
	void CreateHead()
	{
		_head = new Node;
		_head->_next = _head;
		_head->_prev = _head;
	}
		
n个元素构造

创建头结点,然后遍历n次,逐个插入。

		list(int n, const T& value = T())
		{
			CreateHead();//创建头节点
			for (int i = 0; i < n; ++i)
			{
				push_back(value);//依次尾插
			}
		}
迭代器区间构造

因为可能要构造的类型不同,因此诞生了迭代器区间构造,构造一个包含与[first,last]范围相同数量元素的容器。

template <class Iterator>
list(Iterator first, Iterator last)
{
	CreateHead();
	while (first != last)
	{
		push_back(*first);
		++first;
	}
}
拷贝构造函数

先申请一个头节点,并让前驱指针和后继指针都指向自己,然后将所给容器中的数据,通过遍历的方式一个个尾插到新构造的容器后面.
这里也可以调用迭代器区间构造一个临时对象temp,然后与其进行交换。

		list(const list<T>& l)
		{
			CreateHead();
			//list<T> temp(l.begin(), l.end());
			//swap(temp);
			for (const auto& e : l)
			{
				push_back(e); //将容器lt当中的数据一个个尾插到新构造的容器后面
			}
		}
赋值拷贝构造

在这里插入图片描述

不穿引用,通过编译器自动调用list的拷贝构造函数构造一个临时对象,然后调用swap交换。

		list<T>& operator=(list<T> l)
		{
			swap(l);
			return *this;
		}
析构函数

在这里插入图片描述

先调用clear函数清理容器当中的数据,然后将头结点释放,并且置空。

		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}
迭代器相关函数

在这里插入图片描述

begin(const begin)函数返回的是第一个有效数据的迭代器,end函数返回的是最后一个有效数据的下一个位置的迭代器。
在当前情况下,最开始创建的头节点就是最后一个有效数据的下一个位置,即end,而刚开始创建头结点的下一个元素就是第一个有效元素

		// List的迭代器
		//正向迭代器begin
		iterator begin()
		{
			return iterator(_head->_next);
		}

		iterator end()
		{
			return iterator(_head);
		}
		//const正向迭代器begin
		const_iterator begin()const
		{
			return const_iterator(_head->_next);
		}
		//const正向迭代器end
		const_iterator end()const
		{
			return const_iterator(_head);
		}
		//反向迭代器rbegin
		reverse_iterator rbegin()
		{
			return reverse_iterator(end());
		}
		//反向迭代器rend
		reverse_iterator rend()
		{
			return reverse_iterator(begin());
		}
		//const 反向迭代器rbegin
		const_reverse_iterator rbegin()const
		{
			return const_reverse_iterator(end());
		}
		//const 反向迭代器rend
		const_reverse_iterator rend()const
		{
			return const_reverse_iterator(begin());
		}
capacity(容量相关)
size

从第一个有效数据开始,定义一个变量count来计数,到最后一个有效数据的下一个位置为止。

size_t size()const
{
	Node* cur = _head->_next;
	size_t count = 0;
	while (cur != _head)
	{
		count++;
		cur = cur->_next;
	}

	return count;
}
empty

看当前节点的下一个节点是否是自己,如果是自己,那么就没有有效元素。

		bool empty()const
		{
			return _head->_next == _head;
		}
resize

先看当前有效数据个数,如果当前数据有效个数大于resize所传递的参数,那么就要减少有效数据的个数,
否则,当前有效数据个数小于resize所传递的参数时,尾插。

		void resize(size_t newsize, const T& data = T())
		{
			size_t oldsize = size();
			//如果newsize比oldsize小,那么就要缩小有效数据个数
			if (newsize < oldsize)
			{
				pop_back();
				--oldsize;
			}
			else
			{
				while (oldsize < newsize)
				{
					push_back(data);
					++oldsize;
				}
			}

		}
list的元素访问操作

List不支持operator[ ]
front返回第一个有效数据的值
back返回最后一个有效数据的值
front或者back加上const意思不变,唯一不同的就是权限从可读可写变成了只读。

T& front()
{
	return _head->_next->_val;
}

const T& front()const
{
	return _head->_next->_val;
}

T& back()
{
	return _head->_prev->_val;
}

const T& back()const
{
	return _head->_prev->_val;
}
list的插入和删除
insert

在pos位置前插入值为val的节点
新建一个节点,然后保存插入前的pos位置的节点,然后先将新节点插入,然后相邻节点连接新节点。

		// 在pos位置前插入值为val的节点
		iterator insert(iterator pos, const T& val)
		{
			Node* newnode = new Node(val);
			//当前pos位置的节点
			Node* cur = pos._node;
			//先将新节点插入
			newnode->_prev=cur->_prev;
			newnode->_next = cur;

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

			return iterator(newnode);
		}
erase

记录当前要删除的节点,然后记录一下当前节点的前一个位置。然后移除要删除的节点。最后释放掉要删除的节点。

		// 删除pos位置的节点,返回该节点的下一个位置
		iterator erase(iterator pos)
		{
			Node* del = pos._node;
			Node* rdel = pos._node->_prev;
			

			del->_prev->_next = del->_next;
			del->_next->_prev = rdel;

			delete del;
			return iterator(rdel->_next);
		}
push_back和pop_back

复用insert和erase
push_back函数就是在头结点前插入结点,而pop_back就是删除头结点的前一个结点。

void push_back(const T& val)
{
	insert(end(), val);
}

void pop_back()
{
	erase(--end());
}
push_front和pop_front

复用insert和erase
push_front函数就是在第一个有效节点前插入结点,而pop_front就是删除第一个有效节点。

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

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

删除有效数据。在准备删除有效数据前时,要先将这个有效数据的后一个和头结点的next相连接,然后再删除有效数据的节点,然后继续向下进行。

void clear()
{
	Node* cur = _head->_next;

	// 采用头删除删除
	while (cur != _head)
	{
		_head->_next = cur->_next;
		delete cur;
		cur = _head->_next;
	}

	_head->_next = _head->_prev = _head;
}
swap

调用库函数中的
swap函数用于交换两个容器,list容器当中存储的实际上就只有链表的头指针,我们将这两个容器当中的头指针交换即可。

		void swap(bite::list<T>& l)
		{
			std::swap(_head, l._head);
		}

list模拟实现总代码

#pragma once
#pragma once

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

namespace yang
{
	// List的节点类
	template<class T>
	struct ListNode
	{
		ListNode(const T& val = T())
			:_prev(nullptr)
			,_next(nullptr)
			,_val(val)
		{}

		ListNode<T>* _prev;
		ListNode<T>* _next;
		T _val;
	};

	/*
	List 的迭代器
	迭代器有两种实现方式,具体应根据容器底层数据结构实现:
	  1. 原生态指针,比如:vector
	  2. 将原生态指针进行封装,因迭代器使用形式与指针完全相同,因此在自定义的类中必须实现以下方法:
		 1. 指针可以解引用,迭代器的类中必须重载operator*()
		 2. 指针可以通过->访问其所指空间成员,迭代器类中必须重载oprator->()
		 3. 指针可以++向后移动,迭代器类中必须重载operator++()与operator++(int)
			至于operator--()/operator--(int)释放需要重载,根据具体的结构来抉择,双向链表可以向前移动,所以需要重载,如果是forward_list就不需要重载--
		 4. 迭代器需要进行是否相等的比较,因此还需要重载operator==()与operator!=()
	*/
	template<class T, class Ref, class Ptr>
	class ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T, Ref, Ptr> Self;

		// Ref 和 Ptr 类型需要重定义下,实现反向迭代器时需要用到
	public:
		typedef Ref Ref;
		typedef Ptr Ptr;
	public:
		// 构造
		ListIterator(Node* node = nullptr)
			:_node(node)
		{

		}

		// 具有指针类似行为
		Ref operator*()
		{
			return _node->_val;
		}
		Ptr operator->()
		{
			return &_node->_val;
		}

		// 迭代器支持移动
		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 *this;
		}
		// 迭代器支持比较
		bool operator!=(const Self& l)const
		{
			return _node != l._node;
		}
		bool operator==(const Self& l)const
		{
			return _node == l._node;
		}
		
		Node* _node;
	};

	template<class Iterator>
	class ReverseListIterator
	{
		// 注意:此处typename的作用是明确告诉编译器,Ref是Iterator类中的一个类型,而不是静态成员变量
		// 否则编译器编译时就不知道Ref是Iterator中的类型还是静态成员变量
		// 因为静态成员变量也是按照 类名::静态成员变量名 的方式访问的
	public:
		typedef typename Iterator::Ref Ref;
		typedef typename Iterator::Ptr Ptr;
		typedef ReverseListIterator<Iterator> Self;
	public:
		// 构造
		ReverseListIterator(Iterator it)
			:_it(it)
		{

		}

		// 具有指针类似行为
		Ref operator*()
		{
			Iterator temp(_it);
			--temp;
			return *temp;
		}
		Ptr operator->()
		{
			return &(operator*());
		}

		// 迭代器支持移动
		Self& operator++()
		{
			--_it;
			return *this;
		}
		Self operator++(int)
		{
			Self temp(*this);
			--_it;
			return temp;
		}
		Self& operator--()
		{
			++_it;
			return *this;
		}
		Self operator--(int)
		{
			Self temp(*this);
			++_it;
			return temp;
		}
		
		// 迭代器支持比较
		bool operator!=(const Self& l)const
		{
			return _it != l._it;
		}
		bool operator==(const Self& l)const
		{
			return _it == l._it;      
		}

		Iterator _it;
	};

	template<class T>
	class list
	{
		typedef ListNode<T> Node;

	public:
		// 正向迭代器
		typedef ListIterator<T, T&, T*> iterator;
		typedef ListIterator<T, const T&, const T*> const_iterator;

		// 反向迭代器
		typedef ReverseListIterator<iterator> reverse_iterator;
		typedef ReverseListIterator<const_iterator> const_reverse_iterator;

	public:
		// List的构造
		list()
		{
			CreateHead();
		}
		list(int n, const T& value = T())
		{
			CreateHead();//创建头节点
			for (int i = 0; i < n; ++i)
			{
				push_back(value);//依次尾插
			}
		}

		template <class Iterator>
		list(Iterator first, Iterator last)
		{
			CreateHead();
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}
		list(const list<T>& l)
		{
			CreateHead();
			//list<T> temp(l.begin(), l.end());
			//swap(temp);
			for (const auto& e : l)
			{
				push_back(e); //将容器lt当中的数据一个个尾插到新构造的容器后面
			}
		}
		
		list<T>& operator=(list<T> l)
		{

			swap(l);
			return *this;
		}
		
		~list()
		{
			clear();
			delete _head;
			_head = nullptr;
		}
		

		// List的迭代器
				// List的迭代器
		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);
		}

		reverse_iterator rbegin()
		{
			return reverse_iterator(end());
		}

		reverse_iterator rend()
		{
			return reverse_iterator(begin());
		}

		const_reverse_iterator rbegin()const
		{
			return const_reverse_iterator(end());
		}

		const_reverse_iterator rend()const
		{
			return const_reverse_iterator(begin());
		}
		
		// List的容量相关
		size_t size()const
		{
			Node* cur = _head->_next;
			size_t count = 0;
			while (cur != _head)
			{
				++count;
				cur = cur->_next;
			}
			return count;
		}
		bool empty()const
		{
			return _head->_next == _head;
		}
		void resize(size_t newsize, const T& data = T())
		{
			size_t oldsize = size();
			//如果newsize比oldsize小,那么就要缩小有效数据个数
			if (newsize < oldsize)
			{
				pop_back();
				--oldsize;
			}
			else
			{
				while (oldsize < newsize)
				{
					push_back(data);
					++oldsize;
				}
			}

		}
		
		
		// List的元素访问操作
		// 注意:List不支持operator[]
		T& front()
		{
			return _head->_next->_val;
		}

		const T& front()const
		{
			return _head->_next->_val;
		}

		T& back()
		{
			return _head->_prev->_val;
		}

		const T& back()const
		{
			return _head->_prev->_val;
		}
		
		// List的插入和删除
		void push_back(const T& val)
		{
			insert(end(), val);
		}

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

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

		void pop_front()
		{
			erase(begin());
		}
		
		// 在pos位置前插入值为val的节点
		iterator insert(iterator pos, const T& val)
		{
			Node* newnode = new Node(val);
			//当前pos位置的节点
			Node* cur = pos._node;
			//先将新节点插入
			newnode->_prev=cur->_prev;
			newnode->_next = cur;

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

			return iterator(newnode);
		}

		// 删除pos位置的节点,返回该节点的下一个位置
		iterator erase(iterator pos)
		{
			Node* del = pos._node;
			Node* rdel = pos._node->_prev;
			

			del->_prev->_next = del->_next;
			del->_next->_prev = rdel;

			delete del;
			return iterator(rdel->_next);
		}
	
		void clear()
		{
			Node* cur = _head->_next;

			// 采用头删除删除
			while (cur != _head)
			{
				_head->_next = cur->_next;
				delete cur;
				cur = _head->_next;
			}

			_head->_next = _head->_prev = _head;
		}

		void swap(yang::list<T>& l)
		{
			std::swap(_head, l._head);
		}
		
	private:
		void CreateHead()
		{
			_head = new Node;
			_head->_next = _head;
			_head->_prev = _head;
		}
		
	private:
		Node* _head;
	};
}


/
 //对模拟实现的list进行测试
 //正向打印链表
template<class t>
void printlist(const yang::list<t>& l)
{
	auto it = l.begin();
	while (it != l.end())
	{
		cout << *it << " ";
		++it;
	}

	cout << endl;
}

// 测试list的构造
void testbitelist1()
{
	yang::list<int> l1;
	yang::list<int> l2(10, 5);
	printlist(l2);

	int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
	yang::list<int> l3(array, array + sizeof(array) / sizeof(array[0]));
	printlist(l3);

	yang::list<int> l4(l3);
	printlist(l4);

	l1 = l4;
	printlist(l1);
}

// pushback()/popback()/pushfront()/popfront()
void testbitelist2()
{
	// 测试pushback与popback
	yang::list<int> l;
	l.push_back(1);
	l.push_back(2);
	l.push_back(3);
	printlist(l);

	l.pop_back();
	l.pop_back();
	printlist(l);

	l.pop_back();
	cout << l.size() << endl;

	// 测试pushfront与popfront
	l.push_front(1);
	l.push_front(2);
	l.push_front(3);
	printlist(l);

	l.pop_front();
	l.pop_front();
	printlist(l);

	l.pop_front();
	cout << l.size() << endl;
}

// 测试insert和erase
void testbitelist3()
{
	int array[] = { 1, 2, 3, 4, 5 };
	yang::list<int> l(array, array + sizeof(array) / sizeof(array[0]));

	auto pos = l.begin();
	l.insert(l.begin(), 0);
	printlist(l);

	++pos;
	l.insert(pos, 2);
	printlist(l);

	l.erase(l.begin());
	l.erase(pos);
	printlist(l);

	// pos指向的节点已经被删除,pos迭代器失效
	cout << *pos << endl;

	auto it = l.begin();
	while (it != l.end())
	{
		it = l.erase(it);
	}
	cout << l.size() << endl;
}

// 测试反向迭代器
void testbitelist4()
{
	int array[] = { 1, 2, 3, 4, 5 };
	yang::list<int> l(array, array + sizeof(array) / sizeof(array[0]));

	auto rit = l.rbegin();
	while (rit != l.rend())
	{
		cout << *rit << " ";
		++rit;
	}
	cout << endl;

	const yang::list<int> cl(l);
	auto crit = l.rbegin();
	while (crit != l.rend())
	{
		cout << *crit << " ";
		++crit;
	}
	cout << endl;
}


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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

土壤分析仪:解锁土壤奥秘,赋能现代农业的绿色引擎

在广袤无垠的大地上&#xff0c;土壤是生命之源&#xff0c;滋养着万物生长。然而&#xff0c;随着现代农业的快速发展和环境的不断变化&#xff0c;土壤的健康状况日益受到关注。如何科学、精准地了解土壤的性质与养分状况&#xff0c;成为现代农业可持续发展的关键。这时&…

ST-LINK未能串口keil识别的一个可能解决方案(前提驱动安装无问题)

打开这个软件&#xff0c;在点击清除之前&#xff0c;按住单片机复位按钮不放&#xff0c;点击清除按钮&#xff0c;等待3-5秒放开复位按钮&#xff0c;即可清除重置&#xff0c;若提示没识别到&#xff0c;多重复几次&#xff0c;即可重置&#xff0c;重置完成之后再回到烧写软…

兴业严选|朝阳优质好房合集 低至6.3折起~

7月25日&#xff0c;存款挂牌利率迎来今年首次下调。中国工商银行、中国农业银行、中国银行、中国建设银行四家大型商业银行从7月25日起&#xff0c;均下调了人民币存款挂牌利率。这是今年以来大型商业银行首次下调人民币存款利率&#xff0c;也是自2022年9月以来的第五次下调。…

不是ChatGPT模型,第一个GAI是ELIZA,你听说过吗?

人工智能&#xff08;Artificial Intelligence, AI&#xff09;的概念可以追溯到20世纪50年代&#xff0c;当时数学家和计算机科学家开始探讨如何让机器模拟人类智能。1956年&#xff0c;达特茅斯会议被认为是人工智能研究的正式起点。然而&#xff0c;生成式人工智能&#xff…

day7 Excel教程——如何用单元格格式给表格化个妆?(超多干货)

day7 如何用单元格格式给表格化个妆&#xff1f; 目录 1. 单元格内容 Excel中单元格内容分为文本、数值、逻辑值。在没有任何格式下&#xff1a; 文本&#xff1a;左对齐&#xff0c;不能计算 数值&#xff1a;右对齐&#xff0c;可以计算 逻辑值&#xff1a;对/错&#xff0…

XR-Frame 计算相机与场景物体的距离

如下哦 const cameraTransform this.scene.getElementById(camera).getComponent(transform)const modelTransform this.scene.getElementById(yourNodeId).getComponent("transform");if (cameraTransform.worldPosition.distanceTo(modelTransform.worldPosition…

Simulink代码生成:基本算数运算

文章目录 1 引言2 模块使用实例2.1 Add模块2.2 Product模块2.3 Gain模块 3 代码生成4 总结 1 引言 算数运算是Simulink中的一种基本运算&#xff0c;对应C语言中的算数运算符&#xff0c;包括加、减、乘、除和取模运算。本文研究这几种运算在Simulink的使用&#xff0c;以及生…

微服务架构革新:百度Jarvis2.0与云原生技术的力量

作者 | 商业广告平台团队 导读 从十几个模块到上千个微服务&#xff0c;百度如何构建业界最复杂的微服务系统&#xff1f;Jarvis平台&#xff0c;十年磨一剑&#xff0c;集服务治理、配置管理、链路追踪于一体&#xff0c;打造云原生控制中心。Jarvis2.0&#xff0c;多运行时架…

大型分布式B2B2C多用户商城7.0企业版源码分享【java语言、方便二次开发】

项目介绍 项目基于SpringBoot开发&#xff0c;运营端和商户端采用ElementVue&#xff0c;买家使用采用VueIviewnuxt服务端渲染。使用到的中间件有Redis、RabbitMQ、ElasticSearch、FastDFS、Mongodb等。主要功能包括有运营管理、商品管理、订单管理、售后管理、会员管理、财务…

【PyQt5】一文向您详细介绍 QRadioButton() 的作用

【PyQt5】一文向您详细介绍 QRadioButton() 的作用 下滑即可查看博客内容 &#x1f308; 欢迎莅临我的个人主页 &#x1f448;这里是我静心耕耘深度学习领域、真诚分享知识与智慧的小天地&#xff01;&#x1f387; &#x1f393; 博主简介&#xff1a;985高校的普通本硕&am…

windows下安装gcc和make

目录 引言 第一种&#xff1a;自定义安装 下载和安装MinGw 将bin目录添加进环境变量 拷贝mingw-get.exe改名为make.exe 查看gcc和make命令是否安装成功 测试make和makefile 第二种&#xff1a;免安装&#xff0c;解压可用 下载mingw64 配置环境变量 拷贝mingw-get.e…

链码简介及MATLAB提取彩色图像链码

一、链码 链码&#xff08;又称为freeman code&#xff09;是一种通过带有给定方向的单位长度的线段序列来描述轮廓边界的方法,常被用来在图像处理、计算机图形学、模式识别等领域中表示曲线和区域边界。在二维图像中&#xff0c;链码可以表示为一系列的方向码&#xff0c;每个…

基于遗传算法的BP神经网络+代码解析

嗨&#xff0c;我是射手座的程序媛&#xff0c;期待和大家更多的交流与学习&#xff0c;欢迎添加3512724768。 基于遗传算法的BP神经网络代码解析 自己在2024年年初开始时&#xff0c;因为某些原因&#xff0c;了解到了基于遗传算法的神经网络。之前&#xff0c;对遗传算法并…

韦东山瑞士军刀项目自学之复习OS中断相关知识

和STM32无关&#xff0c;单纯是为了秋招复习一下中断的流程&#xff0c;其中涉及到内核态与用户态之间的转换&#xff0c;以及系统调用等等

开源免费软件一键瘦身你的Windows系统-Win11Debloat

开源免费软件一键瘦身你的Windows系统-Win11Debloat 前言 随着 Windows 11 的发布&#xff0c;许多用户发现系统中预装了大量的应用和功能&#xff0c;这些应用和功能可能会影响系统的性能和用户体验。为了帮助用户优化他们的 Windows 11 系统&#xff0c;可以使用一个名为 W…

VBA代码解决方案第十六讲:如何引用工作表(引用工作表的方式)

《VBA代码解决方案》(版权10028096)这套教程是我最早推出的教程&#xff0c;目前已经是第三版修订了。这套教程定位于入门后的提高&#xff0c;在学习这套教程过程中&#xff0c;侧重点是要理解及掌握我的“积木编程”思想。要灵活运用教程中的实例像搭积木一样把自己喜欢的代码…

Mybatis的写增删改查的方法

一、Mybatis环境配置&#xff1a; 环境的配置我在前面的文章里有详细写https://blog.csdn.net/daibadetianshi/article/details/140698718?spm1001.2014.3001.5501https://blog.csdn.net/daibadetianshi/article/details/140698718?spm1001.2014.3001.5501 二、方法之用注解…

探索有赞小程序:揭秘其强大的组件库功能!

有赞小程序组件库是针对有赞小程序开发者提供的一套组件&#xff0c;它提供了丰富的功能和样式&#xff0c;方便开发者快速搭建和定制各种有赞小程序页面。下面将从四个方面对有赞小程序组件库的功能做详细的阐述。 1、UI组件 可实时在线编辑的设计工具——即时设计https://a…

【C++】C++11之右值引用

目录 一、前言 二、概念 2.1 左值和左值引用 2.2 右值和右值引用 三、左值引用与右值引用的区别 四、应用场景 4.1 左值引用的短板&#xff08;将亡值&#xff09; 4.2 右值引用的意义&#xff08;移动语义&#xff09; 五、右值引用引用左值 六、万能引用和完美转发…