目录
[4.4:operator =的重载与swap](#4.4:operator =的重载与swap)
[5.91:测试swap与operator =](#5.91:测试swap与operator =)
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函数可以在所给迭代器之前插入一个新结点,我们来看官方文档

- 创建新节点并且获取当前位置节点与当前节点的前驱节点.
- 当前节点的前驱节点的后继节点指向新节点.
- 新节点的前驱指向当前节点的前驱.
- 新节点的后继指向当前节点.
- 当前节点的前驱指向新节点.
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指向的是自定义类型时,我们可以清晰地发现,此时无法对其进行*,因为内置类型支持流插入,而自定义类型不支持流插入.那么要解决这个问题的话有两种方案.
- 自己写一个流插入.
- 由于数据是公有的,那么我们可以直接访问, *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:list与vector****的对比
vector 与 list 都是 STL 中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同,其主要不同如下:
|-------------------------------|-----------------------------------------------------------------------------------------------|---------------------------------------------------------|
| | vector | list |
| 底 层 结 构 | 动态顺序表,一段连续空间 | 带头结点的双向循环链表 |
| 随 机 访 问 | 支持随机访问,访问某个元素效率 O(1) | 不支持随机访问,访问某个元素 效率 O(N) |
| 插 入 和 删 除 | 任意位置插入和删除效率低,需要搬移元素,时间复杂 度为 O(N) ,插入时有可能需要增容,增容:开辟新空 间,拷贝元素,释放旧空间,导致效率更低. | 任意位置插入和删除效率高,不 需要搬移元素,时间复杂度为 O(1). |
| 空 间 利 用 率 | 底层为连续空间,不容易造成内存碎片,空间利用率 高,缓存利用率高. | 底层节点动态开辟,小节点容易 造成内存碎片,空间利用率低, 缓存利用率低. |
| 迭 代 器 | 原生态指针 | 对原生态指针 ( 节点指针 ) 进行封装 |
| 迭 代 器 失 效 | 在插入元素时,要给所有的迭代器重新赋值,因为插入 元素有可能会导致重新扩容,致使原来迭代器失效,删 除时,当前迭代器需要重新赋值否则会失效. | 插入元素不会导致迭代器失效, 删除元素时,只会导致当前迭代 器失效,其他迭代器不受影响 |
| 使 用 场 景 | 需要高效存储,支持随机访问,不关心插入删除效率. | 大量插入和删除操作,不关心随 机访问. |