深入剖析List的底层实现原理

目录

1:List各个函数接口

2:节点类的实现

3:List类的实现

3.1:基础框架与push_back的实现

3.2:迭代器类的基础版本实现

3.2.1:构造函数

3.2.2:++运算符的重载

3.2.3:--运算符的重载

3.2.4:==运算符的重载

3.2.5:!=运算符的重载

3.2.6:*运算符的重载

3.3:insert

3.4:erase

3.5:push_back和pop_back

3.6:push_front和pop_front

3.7:size与empty的实现

4:细节补充

4.1:->运算符的重载

4.2:const迭代器

4.2.1:第一种写法

4.2.2:第二种写法

4.3:clear与析构函数与拷贝构造函数

[4.4:operator =的重载与swap](#4.4:operator =的重载与swap)

5:相关测试

5.1:基础框架与Push_back的测试

5.2:迭代器的测试

5.3:测试insert

5.4:测试erase

5.5:测试push_back和pop_back

5.6:测试push_front和pop_front

5.7:测试size与empty

5.8:测试operator->

5.9:测试析构函数与拷贝构造函数

[5.91:测试swap与operator =](#5.91:测试swap与operator =)

6:总代码

6.1:List.h

6.2:Test.cpp

7:list与vector的对比


1:List各个函数接口

cpp 复制代码
namespace MyList
{
	//节点类的定义
	template <class T>
	struct ListNode
	{
		ListNode* Previous;
		ListNode* Next;
		T Val;
	};
	//迭代器类的定义
	template<class T, class Ref, class Ptr>
	struct ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T, Ref, Ptr> Self;
		ListIterator(Node* node)
			:_Node(node)
		{
		}
		Node* _Node;
		//前置++
		Self& operator++();
		//后置++
		Self operator++(int);
		Ptr operator->();
		//前置--
		Self& operator--();
		//后置--
		Self operator--(int);
		Ref operator*();
		bool operator !=(const Self& it);
		bool operator ==(const Self& 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;
		List()
		{
			empty_init();
		}
		List(const List<T>& lt);
		list<T>& operator=(list<T> lt);
		iterator begin();
		iterator end();
		const_iterator begin() const;
		const_iterator end() const;
		void push_front(const T& val);
		void push_back(const T& val);
		void insert(iterator pos, const T& val);
		iterator erase(iterator pos);
		void swap(List<T>& lt);
		void pop_front();
		void pop_back();
		void clear();
		void empty_init();
		~list();
	private:
		Node* _Head;
		size_t _Size;
	};
}

2:节点类的实现

我们经常说list在底层实现时就是一个链表,更准确来说,list实际上是一个带头双向循环链表.

而一个结点需要存储的信息有:数据、前一个结点的地址、后一个结点的地址,于是该结点类的成员变量也就出来了(数据、前驱指针、后继指针)。而对于该结点类的成员函数来说,我们只需实现一个构造函数即可。因为该结点类只需要根据数据来构造一个结点即可,而结点的释放则由list的析构函数来完成。

cpp 复制代码
	//节点类的定义
	template <class T>
	struct ListNode
	{
		ListNode* Previous;
		ListNode* Next;
		T Val;
		//构造函数
		ListNode(const T & val = T())
			:Previous(nullptr)
			, Next(nullptr)
			, Val(val)
		{
		}
	};

3:List类的实现

3.1:基础框架与push_back的实现

  • 我们先来快速得搭建一个框架,那么首先就是List类的构造函数还有我们常用的push_back.
  • list是一个带头双向循环链表,在构造一个list对象时,直接申请一个头结点,并让其前驱指针和后继指针都指向自己即可。
cpp 复制代码
namespace MyList
{
	//节点类的定义
	template <class T>
	struct ListNode
	{
		ListNode* Previous;
		ListNode* Next;
		T Val;
		//构造函数
		ListNode(const T & val = T())
			:Previous(nullptr)
			, Next(nullptr)
			, Val(val)
		{
		}
	};

	template <class T>
	class List
	{
		typedef ListNode<T> Node;
	public:
		List()
		{
			//创建哨兵位的头节点
			_Head = new Node();
			_Head->Next = _Head;
			_Head->Previous = _Head;
		}
	private:
		Node* _Head;
	};
}

对于链表的尾插其实很简单,在C语言的数据结构阶段博主细讲了,uu们可以回过头去看看哦~,这里博主直接写步骤了.

  • 构建新节点并且找到尾节点.
  • 1:尾结点的Next指针指向NewNode.
  • 2:NewNode的前驱节点指向Tail.
  • 3:NewNode的后驱节点指向哨兵位.
  • 4:哨兵位的前驱节点指向NewNode.
cpp 复制代码
		void push_back(const T & Val)
		{
			//构建新节点
			Node* NewNode = new Node(Val);
			//找到尾节点
			Node* Tail = _Head->Previous;
			//1:尾结点的Next指针指向NewNode
			Tail->Next = NewNode;
			//2:NewNode的前驱节点指向Tail
			NewNode->Previous = Tail;
			//3 : NewNode的后驱节点指向哨兵位
			NewNode->Next = _Head;
			//4:哨兵位的前驱节点指向NewNode
			_Head->Previous = NewNode;

		}

3.2:迭代器类的基础版本实现

  • 我们之前在实现string类和vector的时候都没有实现一个迭代器类,那么为什么到了List的时候就需要实现一个迭代器类了呢?
  • 因为string和vector对象都将数据存储在了一块连续的内存空间,我们通过指针进行自增、自减以及解引用等操作,就可以对相应位置的数据进行一系列操作,因此string和vector当中的迭代器就是原生指针.

对于list来说,其各个结点在内存当中的位置是随机的,即逻辑结构上是连续的,但物理结构上并不是连续的,我们不能仅通过结点指针的自增、自减以及引用等操作对相应节点的数据进行操作.

  • 而迭代器的意义就是,让使用者可以不必关心容器的底层实现,可以使用简单统一的方式对容器内的数据进行访问.
  • 那么因此list的结点指针的行为不满足迭代器的定义,那么我们可以对这个结点指针进行封装,对结点指针的各种运算符操作进行重载,使得我们可以用和string以及vector当中的迭代器一样的方式使用list当中的迭代器.
  • list迭代器类,实际上就是对结点指针进行了封装,对其各种运算符进行了重载,使得结点指针的各种行为看起来和普通指针一样.

3.2.1:构造函数

cpp 复制代码
	template <class T>
	struct ListIterator
	{
		typedef ListNode<T> Node;
		//构造函数
		ListIterator(Node* node)
			:_Node(node);
		{
		}
		Node* _Node;
	};

3.2.2:++运算符的重载

  • 首先是前置++,前置++原本的作用是先自增再使用.而我们的目的是让结点指针的行为看起来更像普通指针,那么对于结点指针的前置++,我们就应该让结点指针指向后一个节点,然后返回自增后的结点指针即可.
  • 对于后置++,应该先记录当前结点指针的指向,然后让结点指针指向后一个结点,最后返回"自增"前的结点指针即可。
cpp 复制代码
	template <class T>
	struct ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T> Self;
		//前置++
		self operator++()
		{
			_Node = _Node->Next;
			return *this;
		}
		//后置++
		self operator++(int)
		{
			//拷贝一个临时变量
			Self temp = *this;
			_Node = _Node->Next;
			return temp;
		}
	};

3.2.3:--运算符的重载

  • 对于前置- -,先让结点指针指向前一个结点,然后再返回"自减"后的结点指针即可。
  • 而对于后置- -,则先记录当前结点指针的指向,然后让结点指针指向前一个结点,最后返回"自减"前的结点指针即可。
cpp 复制代码
		//前置--
		Self& operator--()
		{
			_Node = _Node->Previous;
			return *this;
		}
		//后置--
		Self operator--(int)
		{
			//拷贝一个临时变量
			Self temp = *this;
			_Node = _Node->Previous;
			return temp;
		}

3.2.4:==运算符的重载

当使用==运算符比较两个迭代器时,我们实际上想知道的是这两个迭代器是否是同一个位置的迭代器,也就是说,判断这两个迭代器当中的结点指针的指向是否相同即可.

cpp 复制代码
bool operator==(const Self& it)
{
	return _Node == it._Node;
}

3.2.5:!=运算符的重载

!=运算符刚好和==运算符的作用相反,判断这两个迭代器当中的结点指针的指向是否不同即可.

cpp 复制代码
		bool operator!=(const Self& it)
		{
			//通过比较节点地址来判断迭代器是否相等
			return _Node != it._Node;
		}

3.2.6:*运算符的重载

当我们使用解引用操作符时,是想得到该位置的数据内容.因此,我们直接返回当前结点指针所指结点的数据即可,但是这里需要使用引用返回,因为解引用后可能需要对数据进行修改.

cpp 复制代码
		T& operator*()
		{
			return _Node->Val;
		}

3.3:insert

insert函数可以在所给迭代器之前插入一个新结点,我们来看官方文档

  • 创建新节点并且获取当前位置节点与当前节点的前驱节点.
  1. 当前节点的前驱节点的后继节点指向新节点.
  2. 新节点的前驱指向当前节点的前驱.
  3. 新节点的后继指向当前节点.
  4. 当前节点的前驱指向新节点.
cpp 复制代码
		void insert(iterator Position,const T &Val)
		{
			//构建新节点
			Node* NewNode = new Node(Val);
			//找到Position位置的节点
			Node* Current = Position._Node;
			//找到Position位置节点的前驱节点
			Node* Prev = Current->Previous;
			//当前节点的前驱指向NewNode
			Prev->Next = NewNode;
			//NewNode的前驱指向Prev
			NewNode->Previous = Prev;
			//NewNode的后驱指向Current
			NewNode->Next = Current;
			//Current的前驱指向NewNode
			Current->Previous = NewNode;
		}

3.4:erase

  • 1:获取当前位置的节点以及该节点的前驱节点和后继节点.
  • 2:当前节点的前驱节点的后继指向当前节点后继节点.
  • 3:当前节点的后继节点的前驱指向当前节点前驱节点.
cpp 复制代码
		iterator erase(iterator Position)
		{
			//找到Position位置的节点
			Node* Current = Position._Node;
			//找到Position位置节点的前驱节点
			Node* Prev = Current->Previous;
			//找到Position位置节点的后继节点
			Node* Next = Current->Next;

			//当前节点的前驱节点的后继指向当前节点后继节点
			Prev->Next = Next;
			//当前节点的后继节点的前驱指向当前节点前驱节点
			Next->Previous = Prev;

			//释放当前节点
			delete Current;

			return iterator(Next);

		}

3.5:push_back和pop_back

push_back和pop_back函数分别用于list的尾插和尾删,在已经实现了insert和erase函数的情况下,可以通过复用函数来实现push_back和pop_back函数。

cpp 复制代码
	void push_back(const T & Val)
	{
        //传统写法
		////构建新节点
		//Node* NewNode = new Node(Val);
		////找到尾节点
		//Node* Tail = _Head->Previous;
		////1:尾结点的Next指针指向NewNode
		//Tail->Next = NewNode;
		////2:NewNode的前驱节点指向Tail
		//NewNode->Previous = Tail;
		////3 : NewNode的后驱节点指向哨兵位
		//NewNode->Next = _Head;
		////4:哨兵位的前驱节点指向NewNode
		//_Head->Previous = NewNode;
        //现代写法
		insert(end(), Val);
	}

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

3.6:push_front和pop_front

  • push_front和pop_front函数也可以复用insert和erase函数来实现。
  • push_front函数就是在第一个有效结点前插入结点,而pop_front就是删除第一个有效结点。
cpp 复制代码
		void push_front(const T & Val)
		{
			insert(begin(), Val);
		}

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

3.7:size与empty的实现

通过给list多设置一个成员变量_Size,用于记录当前容器内的有效数据个数。

cpp 复制代码
size_t size()
{
	return _Size;
}

bool empty()
{
	return _Size == 0;
}

4:细节补充

4.1:->运算符的重载

在将->运算符的重载之前我们来看一段代码

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <vector>
#include "List.h"


struct A{
	int _a1;
	int _a2;
	A(int a1 = 0, int a2 = 0)
		:_a1(a1)
		, _a2(a2)
	{
	}
};

void TestList8()
{
	MyList::List<A> lt;
	lt.push_back(A(1, 2));
	lt.push_back(A(3, 4));
	lt.push_back(A(5, 6));
	MyList::List<A>::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << endl;
		++it;
	}
}

int main ()
{
	TestList8();
	return 0;
}

当it指向的是自定义类型时,我们可以清晰地发现,此时无法对其进行*,因为内置类型支持流插入,而自定义类型不支持流插入.那么要解决这个问题的话有两种方案.

  1. 自己写一个流插入.
  2. 由于数据是公有的,那么我们可以直接访问, *it返回的是A,然后再通过 . 操作符进行访问.
cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <vector>
#include "List.h"

void TestList8()
{
	MyList::List<A> lt;
	lt.push_back(A(1, 2));
	lt.push_back(A(3, 4));
	lt.push_back(A(5, 6));
	MyList::List<A>::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << "{" << (*it)._a1 << ", " << (*it)._a2 << "}" << endl;
		++it;
	}
}

int main ()
{

	TestList8();
	return 0;
}

但是,上面的访问的方式还是有些绕,按照曾经我们学过的方式,我们更偏向于使用 ->的方式来进行访问.

cpp 复制代码
		T* operator->()
		{
			return &_Node->Val;
		}

4.2:const迭代器

按照我们之前对vector和string的做法,const迭代器,我们直接在begin函数和end函数后面加个const即可.

cpp 复制代码
		iterator begin() const
		{
			//返回第一个有效节点
			return iterator(_Head->Next);
		}

		iterator end() const
		{
			//返回哨兵位节点
			return iterator(_Head);
		}
cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <vector>
#include "List.h"

void PrintList(const MyList::List<int>& lt)
{
	MyList::List<int>::iterator it = lt.begin();
	while(it != lt.end())
	{
		*it += 10;
		cout << *it << " ";
		++it;
	}

	cout << endl;
}

void TestList9()
{
	MyList::List<int> lt;
	lt.push_back(10);
	lt.push_back(20);
	lt.push_back(30);
	PrintList(lt);
}

int main ()
{
	TestList9();
	return 0;
}
  • 相信uu们,都发现了这个问题,调用的明明是const迭代器,那么为什么还可以对其进行修改呢?按照之前的理解,const迭代器是不能被修改的.
  • 其实原因在于那个begin函数与end函数后面的那个const,不加const,只有普通对象能够调用,并且返回的是普通的迭代器,但是加了const以后,const对象也能调用,返回的也是普通迭代器,所以这个地方不能这么去写.
  • 因此我们要对其begin()和end()函数进行函数重载.

4.2.1:第一种写法

cpp 复制代码
		const_iterator begin() const 
		{
			//返回第一个有效节点
			return _Head->Next;
		}

		const_iterator end() const
		{
			//返回哨兵位节点
			return _Head;
		}
cpp 复制代码
template <class T>
struct ListConstIterator
{
	typedef ListNode<T> Node;
	typedef ListConstIterator<T> Self;
	//构造函数
	ListConstIterator(Node* node)
		:_Node(node)
	{
	};
	Node* _Node;
	//前置++
	Self& operator++()
	{
		_Node = _Node->Next;
		return *this;
	}
	//后置++
	Self operator++(int)
	{
		//拷贝一个临时变量
		Self temp = *this;
		_Node = _Node->Next;
		return temp;
	}

	//前置--
	Self& operator--()
	{
		_Node = _Node->Previous;
		return *this;
	}
	//后置--
	Self operator--(int)
	{
		//拷贝一个临时变量
		Self temp = *this;
		_Node = _Node->Previous;
		return temp;
	}

	const T& operator*()
	{
		return _Node->Val;
	}

	const T* operator->()
	{
		return &_Node->Val;
	}

	bool operator!=(const Self& it)
	{
		//通过比较节点地址来判断迭代器是否相等
		return _Node != it._Node;
	}

	bool operator==(const Self& it)
	{
		return _Node == it._Node;
	}

};

template <class T>
class List
{
	typedef ListNode<T> Node;
public:
	typedef ListIterator<T> iterator;
	typedef ListConstIterator<T> const_iterator;
public:
	List()
	{
		//创建哨兵位的头节点
		_Head = new Node();
		_Head->Next = _Head;
		_Head->Previous = _Head;
		_Size = 0;
	}
	const_iterator begin() const 
	{
		//返回第一个有效节点
		return _Head->Next;
	}

	const_iterator end() const
	{
		//返回哨兵位节点
		return _Head;
	}

我们可以直接再写一个const迭代器的类,然后类型重命名即可~

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <vector>
#include "List.h"


void PrintList(const MyList::List<int>& lt)
{
	MyList::List<int>::const_iterator it = lt.begin();
	while(it != lt.end())
	{
		//*it += 10;
		cout << *it << " ";
		++it;
	}

	cout << endl;
}

void TestList9()
{
	MyList::List<int> lt;
	lt.push_back(10);
	lt.push_back(20);
	lt.push_back(30);
	PrintList(lt);
}

int main ()
{

	TestList9();
	return 0;
}

此时就没有办法再进行修改了,不过有的uu很好奇,为什么不能按照下面的方式写.

cpp 复制代码
		const iterator begin() const 
		{
			//返回第一个有效节点
			return _Head->Next;
		}

		const iterator end() const
		{
			//返回哨兵位节点
			return _Head;
		}

首先我们要明确一点,我们需要的是迭代器不能够被修改,还是迭代器指向的内容不能够被修改.那么肯定是迭代器指向的内容不能够被修改,const iterator会让迭代器无法被修改,一旦这么写了,那么在遍历的时候无法对其进行++.

4.2.2:第二种写法

如果按照第一种写法来写的话,就要实现两个迭代器类,而且二者基本差不多,这就导致了代码有些冗余,二者唯一的区别是operator * 与 operator->这两个函数的区别.

我们仔细对比一下,发现只有operator * 与operator->的返回值不同,那么对于返回值不同,我们则可以使用模板来实现.

cpp 复制代码
	//迭代器类的定义
	template <class T,class Reference,class Pointer>
	struct ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T, Reference, Pointer> Self;
			//构造函数
		ListIterator(Node* node)
			:_Node(node)
		{
		};
		Node* _Node;
		//前置++
		Self& operator++()
		{
			_Node = _Node->Next;
			return *this;
		}
		//后置++
		Self operator++(int)
		{
			//拷贝一个临时变量
			Self temp = *this;
			_Node = _Node->Next;
			return temp;
		}

		//前置--
		Self& operator--()
		{
			_Node = _Node->Previous;
			return *this;
		}
		//后置--
		Self operator--(int)
		{
			//拷贝一个临时变量
			Self temp = *this;
			_Node = _Node->Previous;
			return temp;
		}

		Reference operator*()
		{
			return _Node->Val;
		}

		Pointer operator->()
		{
			return &_Node->Val;
		}

		bool operator!=(const Self& it)
		{
			//通过比较节点地址来判断迭代器是否相等
			return _Node != it._Node;
		}

		bool operator==(const Self& it)
		{
			return _Node == it._Node;
		}

	};

	template <class T>
	class List
	{
		typedef ListNode<T> Node;
	public:
		typedef ListIterator<T,T&,T*> iterator;
		typedef ListConstIterator<T,const T &,const T *> const_iterator;
	};

4.3:clear与析构函数与拷贝构造函数

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

			}
		}

        void Empty_Init()
		{
			//创建哨兵位的头节点
			_Head = new Node();
			_Head->Next = _Head;
			_Head->Previous = _Head;
			_Size = 0;
		}
        
        //拷贝构造函数
		//lt2(lt1); lt1不变,lt2和lt1内容相同
		List(const List<T>& lt)
		{
			Empty_Init();
			for(auto & element : lt)
			{
				push_back(element);
			}
		}

		~List()
		{
			clear();
			delete _Head;
			_Head = nullptr;
		}

4.4:operator =的重载与swap

cpp 复制代码
		void swap(List<T>& lt)
		{
			std::swap(_Head, lt._Head);
			std::swap(_Size, lt._Size);
		}

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

5:相关测试

5.1:基础框架与Push_back的测试

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <vector>
#include "List.h"
void TestList1()
{
	MyList::List<int> l1;
	l1.push_back(1);
	l1.push_back(2);
	l1.push_back(3);
}

int main ()
{

	TestList1();
	return 0;
}

5.2:迭代器的测试

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include "List.h"

void TestList2()
{
	MyList::List<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	MyList::List<int>::iterator it = lt.begin();
	while(it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	while (--it != lt.begin())
	{
		cout << *it << " ";
	}
}



int main ()
{
	TestList2();
	return 0;
}


5.3:测试insert

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <vector>
#include "List.h"

void TestList3()
{
	MyList::List<int> lt;
	lt.push_back(10);
	lt.push_back(10);
	lt.push_back(10);
	lt.push_back(10);
	lt.push_back(10);
	MyList::List<int>::iterator it = lt.begin();
	lt.insert(it, 100);
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;
}


int main ()
{
	TestList3();
	return 0;
}

5.4:测试erase

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <vector>
#include "List.h"

void TestList4()
{
	MyList::List<int> lt;
	lt.push_back(10);
	lt.push_back(20);
	lt.push_back(30);
	lt.push_back(40);
	lt.push_back(50);
	MyList::List<int>::iterator it = lt.begin();
	while (it != lt.end())
	{
		if (*it == 30)
		{
			it = lt.erase(it);
		}
		else
		{
			++it;
		}
	}
	it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
}

int main ()
{
	TestList4();
	return 0;
}

5.5:测试push_back和pop_back

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <vector>
#include "List.h"
void TestList5()
{
	MyList::List<int> lt;
	lt.push_back(10);
	lt.push_back(20);
	lt.push_back(30);
	lt.push_back(40);
	lt.push_back(50);
	lt.pop_back();
	lt.pop_back();
	MyList::List<int>::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
}

int main ()
{
	TestList5();
	return 0;
}

5.6:测试push_front和pop_front

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <vector>
#include "List.h"


void TestList6()
{
	MyList::List<int> lt;
	lt.push_front(10);
	lt.push_front(20);
	lt.push_front(30);
	lt.push_front(40);
	lt.push_front(50);
	lt.pop_front();
	lt.pop_front();
	MyList::List<int>::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
}

int main ()
{

	TestList6();
	return 0;
}

5.7:测试size与empty

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <vector>
#include "List.h"

void TestList7()
{
	MyList::List<int> lt;
	lt.push_front(10);
	lt.push_front(20);
	lt.push_front(30);
	lt.push_front(40);
	lt.push_front(50);

	cout << "Size: "<< lt.size() << endl;
	cout << "Empty: " << lt.empty() << endl;

}

int main ()
{
	TestList7();
	return 0;
}

5.8:测试operator->

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <vector>
#include "List.h"

void TestList8()
{
	MyList::List<A> lt;
	lt.push_back(A(1, 2));
	lt.push_back(A(3, 4));
	lt.push_back(A(5, 6));
	MyList::List<A>::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << it->_a1 << ":" << it->_a2 << endl;
        cout << it.operator->()->_a1 << ":" << it.operator->()->_a2 << endl;
		++it;
	}
}

int main ()
{

	TestList8();
	return 0;
}

5.9:测试析构函数与拷贝构造函数

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <vector>
#include "List.h"


void TestList10()
{
	MyList::List<string> lt;
	lt.push_back("hello");
	lt.push_back("world");
	lt.push_back("C++");
	MyList::List<string> lt2(lt);
	MyList::List<string>::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	it = lt2.begin();
	while (it != lt2.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

int main ()
{
	TestList10();
	return 0;
}

5.91:测试swap与operator =

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <vector>
#include "List.h"


void TestList11()
{
	MyList::List<int> lt1;
	lt1.push_back(10);
	lt1.push_back(20);
	lt1.push_back(30);
	MyList::List<int> lt2;
	lt2.push_back(100);
	lt2.push_back(200);
	lt2.push_back(300);
	lt2.push_back(400);
	MyList::List<int> lt3 = lt1;
	MyList::List<int>::iterator it = lt1.begin();
	while (it != lt1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	it = lt2.begin();
	while (it != lt2.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	it = lt3.begin();
	while (it != lt3.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	cout << "-----------------------" << endl;
	lt1.swap(lt2);
	it = lt1.begin();
	while (it != lt1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	it = lt2.begin();
	while (it != lt2.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	it = lt3.begin();
	while (it != lt3.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

int main ()
{

	TestList11();
	return 0;
}

6:总代码

6.1:List.h

cpp 复制代码
#pragma once
#include <iostream>
using namespace std;
#include <assert.h>
#include <string>

namespace MyList
{
	//节点类的定义
	template <class T>
	struct ListNode
	{
		ListNode* Previous;
		ListNode* Next;
		T Val;
		//构造函数
		ListNode(const T & val = T())
			:Previous(nullptr)
			,Next(nullptr)
			,Val(val)
		{
		}
	};

	//迭代器类的定义
	template <class T,class Reference,class Pointer>
	struct ListIterator
	{
		typedef ListNode<T> Node;
		typedef ListIterator<T, Reference, Pointer> Self;
			//构造函数
		ListIterator(Node* node)
			:_Node(node)
		{
		};
		Node* _Node;
		//前置++
		Self& operator++()
		{
			_Node = _Node->Next;
			return *this;
		}
		//后置++
		Self operator++(int)
		{
			//拷贝一个临时变量
			Self temp = *this;
			_Node = _Node->Next;
			return temp;
		}

		//前置--
		Self& operator--()
		{
			_Node = _Node->Previous;
			return *this;
		}
		//后置--
		Self operator--(int)
		{
			//拷贝一个临时变量
			Self temp = *this;
			_Node = _Node->Previous;
			return temp;
		}

		Reference operator*()
		{
			return _Node->Val;
		}

		Pointer operator->()
		{
			return &_Node->Val;
		}

		bool operator!=(const Self& it)
		{
			//通过比较节点地址来判断迭代器是否相等
			return _Node != it._Node;
		}

		bool operator==(const Self& it)
		{
			return _Node == it._Node;
		}

	};

	//template <class T>
	//struct ListConstIterator
	//{
	//	typedef ListNode<T> Node;
	//	typedef ListConstIterator<T> Self;
	//	//构造函数
	//	ListConstIterator(Node* node)
	//		:_Node(node)
	//	{
	//	};
	//	Node* _Node;
	//	//前置++
	//	Self& operator++()
	//	{
	//		_Node = _Node->Next;
	//		return *this;
	//	}
	//	//后置++
	//	Self operator++(int)
	//	{
	//		//拷贝一个临时变量
	//		Self temp = *this;
	//		_Node = _Node->Next;
	//		return temp;
	//	}

	//	//前置--
	//	Self& operator--()
	//	{
	//		_Node = _Node->Previous;
	//		return *this;
	//	}
	//	//后置--
	//	Self operator--(int)
	//	{
	//		//拷贝一个临时变量
	//		Self temp = *this;
	//		_Node = _Node->Previous;
	//		return temp;
	//	}

	//	const T& operator*()
	//	{
	//		return _Node->Val;
	//	}

	//	const T* operator->()
	//	{
	//		return &_Node->Val;
	//	}

	//	bool operator!=(const Self& it)
	//	{
	//		//通过比较节点地址来判断迭代器是否相等
	//		return _Node != it._Node;
	//	}

	//	bool operator==(const Self& it)
	//	{
	//		return _Node == it._Node;
	//	}

	//};

	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;
	public:

		void Empty_Init()
		{
			//创建哨兵位的头节点
			_Head = new Node();
			_Head->Next = _Head;
			_Head->Previous = _Head;
			_Size = 0;
		}

		List()
		{
			Empty_Init();
		}

		//拷贝构造函数
		//lt2(lt1); lt1不变,lt2和lt1内容相同
		List(const List<T>& lt)
		{
			Empty_Init();
			for (auto& element : lt)
			{
				push_back(element);
			}
		}

		iterator begin()
		{
			//返回第一个有效节点
			return iterator(_Head->Next);
		}

		iterator end()
		{
			//返回哨兵位节点
			return iterator(_Head);
		}

		const_iterator begin() const 
		{
			//返回第一个有效节点
			return _Head->Next;
		}

		const_iterator end() const
		{
			//返回哨兵位节点
			return _Head;
		}


		void push_back(const T & Val)
		{
			////构建新节点
			//Node* NewNode = new Node(Val);
			////找到尾节点
			//Node* Tail = _Head->Previous;
			////1:尾结点的Next指针指向NewNode
			//Tail->Next = NewNode;
			////2:NewNode的前驱节点指向Tail
			//NewNode->Previous = Tail;
			////3 : NewNode的后驱节点指向哨兵位
			//NewNode->Next = _Head;
			////4:哨兵位的前驱节点指向NewNode
			//_Head->Previous = NewNode;

			insert(end(), Val);
		}

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

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

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

		void insert(iterator Position,const T &Val)
		{
			//构建新节点
			Node* NewNode = new Node(Val);
			//找到Position位置的节点
			Node* Current = Position._Node;
			//找到Position位置节点的前驱节点
			Node* Prev = Current->Previous;
			//当前节点的前驱指向NewNode
			Prev->Next = NewNode;
			//NewNode的前驱指向Prev
			NewNode->Previous = Prev;
			//NewNode的后驱指向Current
			NewNode->Next = Current;
			//Current的前驱指向NewNode
			Current->Previous = NewNode;
			_Size++;
		}

		iterator erase(iterator Position)
		{
			//找到Position位置的节点
			Node* Current = Position._Node;
			//找到Position位置节点的前驱节点
			Node* Prev = Current->Previous;
			//找到Position位置节点的后继节点
			Node* Next = Current->Next;

			//当前节点的前驱节点的后继指向当前节点后继节点
			Prev->Next = Next;
			//当前节点的后继节点的前驱指向当前节点前驱节点
			Next->Previous = Prev;

			//释放当前节点
			delete Current;

			_Size--;
			return iterator(Next);
		}

		size_t size()
		{
			return _Size;
		}

		bool empty()
		{
			return _Size == 0;
		}

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

			}
		}

		void swap(List<T>& lt)
		{
			std::swap(_Head, lt._Head);
			std::swap(_Size, lt._Size);
		}

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

		~List()
		{
			clear();
			delete _Head;
			_Head = nullptr;
		}
	private:
		Node* _Head;
		size_t _Size;
	};
}

6.2:Test.cpp

cpp 复制代码
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;
#include <vector>
#include "List.h"
void TestList1()
{
	MyList::List<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
}

void TestList2()
{
	MyList::List<int> lt;
	lt.push_back(1);
	lt.push_back(2);
	lt.push_back(3);
	MyList::List<int>::iterator it = lt.begin();
	while(it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	while (--it != lt.begin())
	{
		cout << *it << " ";
	}
}

void TestList3()
{
	MyList::List<int> lt;
	lt.push_back(10);
	lt.push_back(10);
	lt.push_back(10);
	lt.push_back(10);
	lt.push_back(10);
	MyList::List<int>::iterator it = lt.begin();
	lt.insert(it, 100);
	for (auto e : lt)
	{
		cout << e << " ";
	}
	cout << endl;
}


void TestList4()
{
	MyList::List<int> lt;
	lt.push_back(10);
	lt.push_back(20);
	lt.push_back(30);
	lt.push_back(40);
	lt.push_back(50);
	MyList::List<int>::iterator it = lt.begin();
	while (it != lt.end())
	{
		if (*it == 30)
		{
			it = lt.erase(it);
		}
		else
		{
			++it;
		}
	}
	it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
}

void TestList5()
{
	MyList::List<int> lt;
	lt.push_back(10);
	lt.push_back(20);
	lt.push_back(30);
	lt.push_back(40);
	lt.push_back(50);
	lt.pop_back();
	lt.pop_back();
	MyList::List<int>::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
}

void TestList6()
{
	MyList::List<int> lt;
	lt.push_front(10);
	lt.push_front(20);
	lt.push_front(30);
	lt.push_front(40);
	lt.push_front(50);
	lt.pop_front();
	lt.pop_front();
	MyList::List<int>::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
}

void TestList7()
{
	MyList::List<int> lt;
	lt.push_front(10);
	lt.push_front(20);
	lt.push_front(30);
	lt.push_front(40);
	lt.push_front(50);

	cout << "Size: "<< lt.size() << endl;
	cout << "Empty: " << lt.empty() << endl;

}

struct A{
	int _a1;
	int _a2;
	A(int a1 = 0, int a2 = 0)
		:_a1(a1)
		, _a2(a2)
	{
	}
};

void TestList8()
{
	MyList::List<A> lt;
	lt.push_back(A(1, 2));
	lt.push_back(A(3, 4));
	lt.push_back(A(5, 6));
	MyList::List<A>::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << it->_a1 << ":" << it->_a2 << endl;
		cout << it.operator->()->_a1 << ":" << it.operator->()->_a2 << endl;
		++it;
	}

}

void PrintList(const MyList::List<int>& lt)
{
	MyList::List<int>::const_iterator it = lt.begin();
	while(it != lt.end())
	{
		//*it += 10;
		cout << *it << " ";
		++it;
	}

	cout << endl;
}

void TestList9()
{
	MyList::List<int> lt;
	lt.push_back(10);
	lt.push_back(20);
	lt.push_back(30);
	PrintList(lt);
}

void TestList10()
{
	MyList::List<string> lt;
	lt.push_back("hello");
	lt.push_back("world");
	lt.push_back("C++");
	MyList::List<string> lt2(lt);
	MyList::List<string>::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	it = lt2.begin();
	while (it != lt2.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

void TestList11()
{
	MyList::List<int> lt1;
	lt1.push_back(10);
	lt1.push_back(20);
	lt1.push_back(30);
	MyList::List<int> lt2;
	lt2.push_back(100);
	lt2.push_back(200);
	lt2.push_back(300);
	lt2.push_back(400);
	MyList::List<int> lt3 = lt1;
	MyList::List<int>::iterator it = lt1.begin();
	while (it != lt1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	it = lt2.begin();
	while (it != lt2.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	it = lt3.begin();
	while (it != lt3.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	cout << "-----------------------" << endl;
	lt1.swap(lt2);
	it = lt1.begin();
	while (it != lt1.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	it = lt2.begin();
	while (it != lt2.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	it = lt3.begin();
	while (it != lt3.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

int main ()
{

	//TestList1();
	//TestList2();
	//TestList3();
	//TestList4();
	//TestList5();
	//TestList6();
	//TestList7();
	//TestList8();
	//TestList9();
	//TestList10();
	TestList11();
	return 0;
}

7:listvector****的对比

vector 与 list 都是 STL 中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同,其主要不同如下:

|-------------------------------|-----------------------------------------------------------------------------------------------|---------------------------------------------------------|
| | vector | list |
| | 动态顺序表,一段连续空间 | 带头结点的双向循环链表 |
| 访 | 支持随机访问,访问某个元素效率 O(1) | 不支持随机访问,访问某个元素 效率 O(N) |
| | 任意位置插入和删除效率低,需要搬移元素,时间复杂 度为 O(N) ,插入时有可能需要增容,增容:开辟新空 间,拷贝元素,释放旧空间,导致效率更低. | 任意位置插入和删除效率高,不 需要搬移元素,时间复杂度为 O(1). |
| | 底层为连续空间,不容易造成内存碎片,空间利用率 高,缓存利用率高. | 底层节点动态开辟,小节点容易 造成内存碎片,空间利用率低, 缓存利用率低. |
| | 原生态指针 | 对原生态指针 ( 节点指针 ) 进行封装 |
| | 在插入元素时,要给所有的迭代器重新赋值,因为插入 元素有可能会导致重新扩容,致使原来迭代器失效,删 除时,当前迭代器需要重新赋值否则会失效. | 插入元素不会导致迭代器失效, 删除元素时,只会导致当前迭代 器失效,其他迭代器不受影响 |
| 使 | 需要高效存储,支持随机访问,不关心插入删除效率. | 大量插入和删除操作,不关心随 机访问. |

相关推荐
艾莉丝努力练剑2 小时前
【QT】Qt 从零上手:Hello World、项目文件与实战避坑指南
linux·运维·开发语言·c++·qt·继承·qt5
HABuo2 小时前
【linux进程控制(二)】进程等待-->死亡的子进程是如何被父进程等待回收的?
linux·运维·服务器·c语言·c++·ubuntu·centos
小龙报2 小时前
【算法通关指南:算法基础篇 】贪心专题之简单贪心:1.最大子段和 2.纪念品分组
c语言·数据结构·c++·算法·ios·贪心算法·动态规划
君义_noip10 小时前
信息学奥赛一本通 1661:有趣的数列 | 洛谷 P3200 [HNOI2009] 有趣的数列
c++·算法·组合数学·信息学奥赛·csp-s
hele_two12 小时前
快速幂算法
c++·python·算法
OopspoO12 小时前
C++杂记——Name Mangling
c++
yuanmenghao12 小时前
车载Linux 系统问题定位方法论与实战系列 - 车载 Linux 平台问题定位规范
linux·运维·服务器·网络·c++
小羊羊Python12 小时前
SoundMaze v1.0.1正式发布!
开发语言·c++
码小猿的CPP工坊16 小时前
C++软件开发之内存泄漏闭坑方法
开发语言·c++