文章目录
- 一、认识list
- 二、list的使用
-
- [2.1 constructor(构造函数)](#2.1 constructor(构造函数))
- [2.2 Iterators](#2.2 Iterators)
-
- [2.2.1 迭代器的分类:](#2.2.1 迭代器的分类:)
- [2.3 Capacity(容量相关)](#2.3 Capacity(容量相关))
- [2.4 Element access(元素访问)](#2.4 Element access(元素访问))
- [2.5 Modifiers(链表修改)](#2.5 Modifiers(链表修改))
- [2.6 Operations(对链表的一些操作)](#2.6 Operations(对链表的一些操作))
- 三、list的底层实现
-
- [✨ list的节点:ListNode](#✨ list的节点:ListNode)
- [✨ list的成员变量:](#✨ list的成员变量:)
- [⭐ list 的迭代器:](#⭐ list 的迭代器:)
- [✨ list的成员函数:](#✨ list的成员函数:)
- [四、list 容器的模拟实现整体代码](#四、list 容器的模拟实现整体代码)
一、认识list
- list 是一种序列容器,可以在序列中的任何位置进行
常数时间的插入 和删除 操作,并且支持双向迭代。 - list容器是用双向链表实现的 ;双向链表的每个元素存储在互不相关的独立节点中。通过每个节点的前驱和后继指针链接,内部保持了元素的顺序。
- 它和单向链表(forward_list)非常相似:主要区别在于单向链表对象是单链表,因此只能向前迭代,但相对更小且更高效。
- 与其他基本标准序列容器(array, vector and deque )相比,list在插入、提取和移动容器中任何位置的元素时表现通常更好,尤其是在已经获得迭代器的情况下,因此在频繁使用这些操作的算法(如排序算法)中也表现更佳。
- list和单向链表相比其他序列容器的主要缺点 是它们无法通过位置直接访问元素;例如,要访问列表中的第六个元素,必须从已知位置(如开头或结尾)迭代到该位置,这需要线性时间。它们还会消耗一些额外的内存来保存与每个元素相关的链接信息。
list 的详细介绍请参考:list
二、list的使用
2.1 constructor(构造函数)
| 构造函数 | 接口说明 |
|---|---|
| list (size_type n, const value_type& val =value_type()) | 构造的list中包含n个值为val的元素 |
| list() | 构造空的list |
| list (const list& x) | 拷贝构造函数 |
| list (InputIterator first, InputIterator last) | 用[first, last)区间中的元素构造 |
注意: value_type表示第一个模板参数(T),size_type表示无符号整型。
2.2 Iterators
大家可暂时 将迭代器理解成一个指针,该指针指向list中的某个节点。
| 函数声明 | 接口说明 |
|---|---|
| begin +end | 返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器 |
| rbegin+ rend | 返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的reverse_iterator,即begin位置 |
注意:
begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
📖示例:利用迭代器遍历链表
cpp
void test_list1()
{
//迭代器
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
//打印
list<int>::iterator it = l1.begin();
while (it != l1.end())
{
cout << *it << " ";
it++;
}
cout << endl;
//支持迭代器同样支持范围for
for (auto ch : l1)
{
cout << ch << " ";
}
cout << endl;
}
list的迭代器不支持+或-,因为底层不是原生指针了,像string和vector的迭代器可以用原生指针实现,是因为其底层物理空间是连续的。
2.2.1 迭代器的分类:
- 按功能分:
| 迭代器 | 说明 |
|---|---|
| iterator | 正向迭代器 |
| reverse_iterator | 反向迭代器 |
| const_iterator | const正向迭代器 |
| coonst_reverse_iterator | const反向迭代器 |
- 按性质分:
| 迭代器 | 举例 | 支持功能 |
|---|---|---|
| 单向迭代器 | forwad_list/unordered_map | 只支持++ |
| 双向迭代器 | list/map/set | 支持++/-- |
| 随机迭代器 | vector/string | 支持++/--/+/- |
| input/output |
说明:
这些迭代器都是包含的关系,单向是特殊的双向,双向是特殊的随机。
性质由容器的底层结构决定,底层结构又决定可以使用哪些算法(algorithm),比如 sort只能使用随机迭代器, reverse(逆置)支持双向迭代器,随机迭代器也能用,find(查找)使用的是input迭代器。
cpp
void test_list2()
{
//迭代器
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
//sort(l1.begin(),l1.end()); 会报错
string s1("asenncewoc");
sort(s1.begin(), s1.end());
cout << s1 << endl;
}
2.3 Capacity(容量相关)
| 函数声明 | 接口说明 |
|---|---|
| empty | 检测list是否为空,是返回true,否则返回false |
| size | 返回list中有效节点的个数 |
list没有扩容。
2.4 Element access(元素访问)
| 函数声明 | 接口说明 |
|---|---|
| front | 返回list的第一个节点中值的引用 |
| back | 返回list的最后一个节点中值的引用 |
📖示例:
cpp
void test_list3()
{
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
cout << l1.front() << endl; //1
cout << l1.back() << endl; //4
}
2.5 Modifiers(链表修改)
| 函数声明 | 接口说明 |
|---|---|
| push_front | 在list首元素前插入值为val的元素 |
| pop_front | 删除list中第一个元素 |
| push_back | 在list尾部插入值为val的元素 |
| emplace_back | 构造并在末尾插入元素 |
| pop_back | 删除list中最后一个元素 |
| insert | 在list position 位置前插入值为val的元素 |
| erase | 删除list position位置的元素 |
| swap | 交换两个list中的元素 |
| clear | 清空list中的有效元素 |
说明:
insert 插入元素并不会导致迭代器失效 ,因为相较于 vector 中的 insert,list 中的 insert 并不会去扩容挪动数据,而 vector 中的 insert 可能会进行扩容挪动数据,最终导致迭代器失效。erase会导致迭代器失效,失效的只有指向被删除节点的迭代器,其他迭代器不会受到影响。
📖erase:
cpp
void test_list4()
{
//迭代器
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
//删除下标为3位置处的元素
//l1.erase(it+3); 这样list不支持了
list<int>::iterator it = l1.begin();
int k=3;
while(k)
{
k--;
it++;
}
l1.erase(it);
for (auto ch:l1)
{
cout << ch << " ";
}
//运行结果:1 2 3
cout << endl;
}
📖emplace_back和push_back的区别:
cpp
void test_list5()
{
//emplace_back相比于push_back更高效一点
//emplace_back是模板的可变参数
list<A> l2;
A aa1(2,2);
l2.push_back(aa1);
l2.push_back(A(2,2)); //传入匿名对象
l2.push_back(3,3); //不支持两个参数,这是push_back与emplace_back的区别所在
l2.emplace_back(aa1);
l2.emplace_back(A(3,3));
l2.emplace_back(4,4); //支持
}
📖insert:
cpp
void test_list7()
{
//iterator insert (iterator position, const value_type& val);
//如果想在下标为3前插入数据
//不能像这样了,l1.insert(it+3,4);
auto It = l1.begin();
int k = 3;
while (k)
{
It++;
k--;
}
l1.insert(It, 5);
for (auto ch : l1)
{
cout << ch << " ";
}
cout << endl;
}
2.6 Operations(对链表的一些操作)
| 函数声明 | 接口说明 |
|---|---|
| reverse | 对链表进行逆置 |
| sort | 对链表中的元素进行排序 |
| merge | 对两个有序的链表进行合并,得到一个有序的链表 |
| unique | 对链表中的元素去重,前提链表必须有序 |
| remove | 删除具有特定值的节点 |
| splice | 将 A 链表中的节点转移到 B 链表,A链表中的节点会删除。 |
说明 :
链表逆置可以使用 list 自身的接口,也可以使用算法库中的 reverse,二者没有什么区别。链表排序只能使用 list 自身的 sort 接口(底层是利用归并排序),不能使用算法库的 sort,因为算法库中的 sort 底层是通过快排来实现的,而快排中会涉及到三数取中需要迭代器 - 迭代器,而list迭代器不支持。在Debug版本下,算法库中的sort不及list中的sort,但在release版本下,算法库中的sort速度远快于list中的sort。
所以当我们要对链表排序时,可以先将list中的数据拷贝到vector中,在vector中排序,排完序后再拷贝回list。
📖merge:
cpp
void test_list8()
{
//merge合并list
list<double> first, second;
first.push_back(3.1);
first.push_back(2.2);
first.push_back(2.9);
second.push_back(3.7);
second.push_back(7.1);
second.push_back(1.4);
first.sort();//默认排升序
second.sort();
first.merge(second);
// (secon现在为空了)
cout << "first contains:";
for (list<double>::iterator it = first.begin(); it != first.end(); ++it)
cout << ' ' << *it;
cout << '\n';
}
📖 unique:
cpp
void test_list9()
{
//unique删除重复元素:要求数据必须有序
//底层去重可以用双指针解决
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
l1.push_back(4);
l1.push_back(5);
l1.push_back(9);
l1.push_back(7);
l1.sort();
l1.unique();
for (list<int>::iterator it = l1.begin(); it != l1.end(); ++it)
cout << ' ' << *it;
cout << '\n';
}
📖splice:
cpp
void test_list10()
{
//splice意思是剪接:Transfer elements from list to list
list<int> mylist1, mylist2;
list<int>::iterator it;
//插入数据
for (int i = 1; i <= 4; ++i)
mylist1.push_back(i); // mylist1: 1 2 3 4
for (int i = 1; i <= 3; ++i)
mylist2.push_back(i * 10); // mylist2: 10 20 30
it = mylist1.begin();
++it;
//将mylist2的所有节点转移到mylist1中it指向的节点后。
mylist1.splice(it, mylist2); // mylist1: 1 10 20 30 2 3 4
// mylist2 (empty)
// "it" still points to 2 (the 5th element)
int k = 0;
cin >> k;
list<int>::iterator lt = find(mylist1.begin(),mylist1.end(),k);
//void splice (iterator position, list& x, iterator i);
//仅将x指向i的元素传输到容器中。
mylist1.splice(mylist1.begin(),mylist1,lt);
for (list<int>::iterator it = mylist1.begin(); it != mylist1.end(); ++it)
cout << ' ' << *it;
cout << '\n';
int c = 0;
cin >> c;
list<int>::iterator at = find(mylist1.begin(), mylist1.end(), c);
//void splice (iterator position, list& x, iterator first, iterator last);
//将范围[first,last]从x传输到容器中。
mylist1.splice(mylist1.begin(), mylist1, at,mylist1.end());
for (list<int>::iterator It = mylist1.begin(); It != mylist1.end(); ++It)
cout << ' ' << *It;
cout << '\n';
}
三、list的底层实现
✨ list的节点:ListNode
list底层就是一个双向链表,它是通过一个一个的节点连接而成的。每个节点是由一个结构体封装的,节点包括指针域,有前驱指针prev(指向前一个节点)和后驱指针next(指向后一个节点),以及数据域_data,存储数据。

cpp
template<class T>
struct ListNode
{
//构造
ListNode(const T& data=T())
:_data(data),
_next(nullptr),
_prev(nullptr)
{
}
T _data;
ListNode<T>* _next;
ListNode<T>* _prev;
};
编译器默认生成的构造函数达不到我们的要求,需要自己实现。在这里并没有使用class定义节点类,因为节点是要经常访问的,而struct默认访问权限是public,class默认访问权限是private,所以使用struct定义更直观。
将这个类加上template<class T>后,就能够实现节点存储不同类型的数据,这也是C++模板的好处。
✨ list的成员变量:
设计思路:ListNode只是一个单独的节点,要想对一个链表操作,需要定义一个list类,对链表的各种操作,都在这个类里面实现。
cpp
template<class T>
class list
{
public:
typedef ListNode<T> Node;
private:
Node* _head;
size_t _size;
}
list的成员变量有一个指针_head,指向list的头节点,还有size,表示list存在几个有效的节点。
因为list中有指针_head,指向list的头节点,需要手动写构造函数,否则编译器默认生成的构造会将_head初始化为随机值,导致程序运行时出现未定义行为。
⭐ list 的迭代器:
设计思路 :list底层的物理空间不是连续的,用原生指针实现list迭代器就不行了,可以将迭代器封装成一个类,在类中重载++,- -,解引用*等。迭代器类中的成员变量只有结点类类型的指针 _node,因为迭代器的本质就是指针。
🌸list_iterator:
cpp
template<class T>
struct list_iterator
{
//结点类的类型取的别名是 Node
typedef ListNode<T> Node;
//为迭代器类取的别名是Self。
typedef list_iterator<T> Self;
//构造函数
list_iterator(Node* node)
:_node(node)
{
}
//解引用希望得到的是Node中的data
T& operator*()
{
return _node->_data;
}
//迭代器指向下一个节点
//返回的是迭代器类型
Self operator++()
{
_node=_node->_next;
return *this;//解引用得到指向下一个节点的迭代器
}
Self operator--()
{
_node=_node->_prev;
return *this;
}
//后置++,返回的是++之前的迭代器
Self operator++(int)
{
Self tmp(*this);
_node=_node->_next;
return tmp;
}
Self operator--(int)
{
Self tmp(*this);
_node=_node->_prev;
return tmp;
}
T* operator->()
{
return &(_node->data);
}
bool operator!=(const Self& s) const
{
//比较迭代器指向的节点的地址是否一样
return _node!=s._node;
}
bool operator==(const Self& s) const
{
return _node==s._node;
}
Node* _node;//指向节点的指针
};
注意 :这里的类名不能直接命名为iterator,因为每种容器的迭代器底层实现都有所不同,即可能会为每一种容器都单独实现一个迭代器类,如果都直接使用 iterator,会导致命名冲突,命名为list_iterator表示实现的是list的迭代器。
⭐list的iterator是否要实现拷贝构造、析构函数、赋值重载?
其中重载operator++(int)时,即后置++,会调用到iterator的拷贝构造,但是iterator并不用实现拷贝构造,编译器默认生成的浅拷贝即可 。函数结束后后调用编译器默认生成的析构函数,为浅析构,仅销毁 _node 指针本身 ,而不会释放_node指向的 Node 节点,这就不会有浅拷贝常见的问题:同一块空间被释放两次。tmp 是函数内的局部对象,存储在栈内存中,栈内存由编译器自动管理。
list_iterator不用自己实现拷贝构造和析构,这恰好符合迭代器的设计要求,迭代器的作用是指向节点、提供访问接口 ,而非直接对list中的节点操作,节点的创建和释放是由 list 容器负责。

🌸list_const_iterator:
cpp
template<class T>
struct list_const_iterator
{
typedef ListNode<T> Node;
typedef list_const_iterator<T> Self;
//解引用希望得到的是Node中的data
const T& operator*()
{
return _node->_data;
}
//迭代器指向下一个节点
//返回的是迭代器类型
Self operator++()
{
_node=_node->_next;
return *this;//解引用得到指向下一个节点的迭代器
}
Self operator--()
{
_node=_node->_prev;
return *this;
}
//后置++,返回的是++之前的迭代器
Self operator++(int)
{
Self tmp(*this);
_node=_node->_next;
return tmp;
}
Self operator--(int)
{
Self tmp(*this);
_node=_node->_prev;
return tmp;
}
const T* operator->()
{
return &(_node->data);
}
bool operator!=(const Self& s) const
{
//比较迭代器指向的节点的地址是否一样
return _node!=s._node;
}
bool operator==(const Self& s) const
{
return _node==s._node;
}
Node* _node;//指向节点的指针
};
🌸关于const_iterator中间为什么要加_?
- 第一个原因是:
C++规定:const_iterator表示 "常量迭代器"(只能读取元素,不能修改元素),与普通的 iterator(可读可写)形成对比。 - 第二个原因是:避免与
const iterator混淆。
const_iterator:迭代器本身可以移动(++/-- --),但通过它访问的元素是 const 的(不能修改)。
const iterator:迭代器本身是 const 的(不能移动),但通过它访问的元素可以修改(如果元素本身非 const)。
⭐迭代器的模板化复用:
上面实现的list_iterator和list_const_iterator在很多地方存在冗余,只有operator和operator->的返回值类型不同,当是list_iterator类时,它们的返回值类型是T&和T*,当是list_const_iterator类时,它们的返回值类型是const T&和const T*。我们可以将operator 和operator->的返回值类型设置成迭代器类的模板参数,当需要的返回值类型是T&和T时,就传入模板参数T&和T 。
所以我们定义一个通用的迭代器模板类 ,通过模板参数 Ref(引用类型)和Ptr(指针类型),统一 operator* 和 operator-> 的实现,同时适配普通迭代器和 const 迭代器。
cpp
template<class T,class Ref,class Ptr>
struct _list_iterator
{
typedef ListNode<T> Node;
typedef _list_iterator<T,Ref,Ptr> Self;
typedef Ref reference;
typedef Ptr pointer;
//构造函数
_list_iterator(Node* node)
:_node(node)
{
}
reference operator*()
{
return _node->_data;
}
//重载++
Self& operator++()
{
_node = _node->_next;
return *this;
}
Self operator++(int)
{
Self tmp(*this);
_node = _node->_next;
return tmp;
}
Self& operator--()
{
this->_node = _node->_prev;
return *this;
}
//后置--
Self operator--(int)
{
Self tmp(*this);
this->_node = _node->_prev;
return tmp;
}
//重载!=
bool operator!=(const Self& s) const
{
return _node != s._node;
}
//重载->
pointer operator->()
{
//如果链表中的节点中的数据为自定义类型,需要对链表中的节点的数据解引用才能访问
return &(_node->_data);
}
Node* _node;
};
✨ list的成员函数:
🌀list(构造):
完成对链表对象的初始化,即得到一个空的新链表。
cpp
//默认构造
list()
{
_head=new Node;//为头节点申请合法的空间,并构造一个节点
_head->next=_head;
_head->prev=_head;
}
new Node先调用operator new申请空间 ,再在申请的空间上调用Node的构造。

🌀迭代器相关:
cpp
typedef _list_iterator<T, T&, T*> iterator;
typedef _list_iterator<T, const T&, const T*> const_iterator;
//可读可写
iterator begin()
{
//写法一:
/*iterator it(_head->_next);
return it;*/
//写法二:
//return it(_head->_next);
//写法三:
return _head->_next;//会进行隐式类型转换:单参数类型的构造函数支持隐式类型转换
}
iterator end()//最后一个数据的下一个位置,即哨兵位的头节点
{
return _head;
}
//只读
const_iterator begin() const
{
return _head->_next;
}
const_iterator end() const
{
return _head;
}
🌀insert:
cpp
iterator insert(iterator pos,const T& x)
{
Node* newnode = new Node(x);
//在it迭代器之前插入
Node* pcur = pos._node;
Node* prev = pcur->_prev;
//prev newnode pcur
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = pcur;
pcur->_prev = newnode;
_size++;
//C++标准规定:返回刚插入的元素的迭代器
return newnode;
}
🌀push_back:
复用insert。
cpp
void push_back(const T& x)
{
insert(end(),x);
}
🌀push_front:
复用insert。
cpp
void push_front(const T& x)
{
insert(begin(), x);
}
🌀erase:
cpp
iterator erase(iterator pos)
{
assert(pos!=end());
Node* pcur = pos._node;
Node* next = pcur->_next;
Node* prev = pcur->_prev;
//pcur pos next
prev->_next = next;
next->_prev = prev;
delete pcur;
_size--;
//返回下一个位置的迭代器,因为删除后那个位置的迭代器失效了。
return next;
}
🌀pop_back:
复用erase。
cpp
void pop_back()
{
erase(--(this->end()));
}
🌀pop_front:
复用erase。
cpp
void pop_front()
{
erase(this->begin());
}
🌀empty:
cpp
//判空
bool empty() const
{
return _size == 0;
}
🌀size:
cpp
size_t size() const
{
return _size;
}
⭐clear:
通过遍历节点的方式,逐个释放节点,只保留头节点。
法一:依赖实现的erase,实现链表的清空。
cpp
void clear()
{
auto it = this->begin();
while(it!=this->end())
{
it=erase(it);
}
}
法二:不依赖实现的erase。
cpp
//清空链表
void clear()
{
if (_head->_next == _head) {
return;
}
Node* pcur = _head->_next;
while (pcur != _head)
{
Node* next = pcur->_next;//保存下一个节点
delete pcur;
pcur = next;
}
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
⭐~list(析构):
通过复用clear,将链表清空,然会释放头指针,最后将头指针置空。
cpp
~list()
{
clear();
delete _head;
_head = nullptr;
}
⭐list(拷贝构造-深拷贝):
拷贝构造是用一个已有对象去构造出另一个对象,首先将待构造对象进行初始化,然后通过复用push_back,将数据尾插到新链表,达到深拷贝的目的。
cpp
void empty_list()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
//拷贝构造(深拷贝)现代写法
list(list<T>& x)
{
//创建一个新的空链表,只有头节点
empty_list();
for (iterator it = x.begin(); it != x.end(); it++)
{
push_back(*it);
}
}
⭐operator=:
cpp
void swap(list<T>& t1, list<T>& t2)
{
std::swap(t1._head, t2._head);
std::swap(t1._size, t2._size);
}
//重载赋值
list<T>& operator=(list<T> t)
{
swap(*this, t);
return *this;
}
四、list 容器的模拟实现整体代码
🌈list.h:
cpp
#pragma once
#include<iostream>
#include<assert.h>
using std::cout;
using std::cin;
using std::endl;
using std::ostream;
using std::istream;
namespace hwy_list
{
template<class T>
struct ListNode
{
//构造函数
ListNode(const T& val = T())// 带默认参数的构造函数
//若 T 是内置类型(如 int、double):T() 会执行 "零初始化",因此 _data 被初始化为 0(int)、0.0(double)等。
//若 T 是指针类型(如 int*):T() 会初始化为 nullptr,因此 _data(指针)被初始化为空指针。
//若 T 是自定义类型(如 string、AA):T() 会调用 T 的默认构造函数,_data 的值由 T 的默认构造函数决定。
:_data(val),
_prev(nullptr),
_next(nullptr)
{
}
T _data;
ListNode* _prev;
ListNode* _next;
};
template<class T, class Ref, class Ptr>
struct list_iterator
{
typedef ListNode<T> Node;
typedef list_iterator<T, Ref, Ptr> Self;
typedef Ref reference;
typedef Ptr ptrainer;
//构造函数,指向一个节点
list_iterator(Node* node)
:_node(node)
{
}
//重载*
reference operator*()
{
return _node->_data;
}
Self& operator++()
{
//获取下一个节点的地址
Self tmp(*this);//会调用拷贝构造,是浅拷贝
_node = _node->_next;
return *this;
}
Self& operator--()
{
_node = _node->_prev;
return *this;
}
//后置--
Self operator--(int)
{
//返回修改以前的迭代器
Self tmp(*this);
_node = _node->_prev;
return tmp;
}
//后置++
Self operator++(int)
{
//返回之前的迭代器
Self tmp(*this);
_node = _node->_next;
return tmp;//返回拷贝构造的临时对象(浅拷贝)
}
bool operator!= (const Self& s)
{
return _node != s._node;
}
//返回迭代器指向的节点中的_data的地址
ptrainer operator->()
{
return &(_node->_data);
}
Node* _node;//_node是一个指针,指向一个节点
};
//创建链表管理类
template<class T>
class list
{
public:
typedef ListNode<T> Node;//1.先调用operator new申请空间 2.在在申请的空间上执行构造
//构造函数
typedef list_iterator<T, T&, T*> iterator;
typedef list_iterator<T, const T&, const T*> const_iterator;
void empty_list()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
list()
{
empty_list();
}
//尾插,即最后一个节点后插入
void push_back(const T& x)
{
insert(end(), x);
}
//在pos位置前面插入节点
iterator insert(iterator pos, const T& x)
{
Node* newnode = new Node(x);
Node* pcur = pos._node;
Node* prev = pcur->_prev;
//prev newnode next
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = pcur;
pcur->_prev = newnode;
_size++;
return newnode;
}
//删除pos位的节点,该节点就失效了
iterator erase(iterator pos)
{
assert(pos != end());
Node* pcur = pos._node;
Node* next = pcur->_next;
Node* prev = pcur->_prev;
//prev newnode next
prev->_next = next;
next->_prev = prev;
delete pcur;
_size--;
return next;//隐式类型转换
}
//头删
void pop_back()
{
erase(begin());
}
//头插
void push_front(const T& x)
{
insert(begin(),x);
}
void pop_front()
{
erase(--(end()));
}
//普通迭代器
iterator begin()
{
iterator it(_head->_next);//传入一个节点的地址
return it;
//return _head->_next;//会进行隐式类型转换
}
iterator end()//最后一个数据的下一个位置,即哨兵位的头节点
{
return _head;
}
//const迭代器
const_iterator begin() const
{
const_iterator it(_head->_next);//传入一个节点的地址
return it;
//return _head->_next;//会进行隐式类型转换
}
const_iterator end() const//最后一个数据的下一个位置,即哨兵位的头节点
{
return _head;
}
size_t size() const
{
return _size;
}
bool empty()
{
return _size == 0;
}
//拷贝构造(深拷贝)
list(list<T>& x)
{
//创建一个新的空链表,只有头节点
empty_list();
for (iterator it = x.begin(); it != x.end(); it++)
{
push_back(*it);
}
}
void swap(list<T>& t1, list<T>& t2)
{
std::swap(t1._head, t2._head);
std::swap(t1._size, t2._size);
}
//重载赋值
list<T>& operator=(list<T> t)
{
swap(*this, t);
return *this;
}
//清空链表
void clear()
{
if (_head->_next == _head) {
return;
}
Node* pcur = _head->_next;
while (pcur != _head)
{
Node* next = pcur->_next;//保存下一个节点
delete pcur;
pcur = next;
}
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
//析构
~list()
{
//delete只能作用于指针
clear();
delete _head;
_head = nullptr;
}
private:
Node* _head;
size_t _size;
};
template<class Container>
void print_container(const Container& v)
{
//传入的是const迭代器,需要调用const迭代器。
//所以需要实现const迭代器
typename Container::const_iterator lt = v.begin();
//auto lt = v.begin();
while (lt != v.end())
{
//const 迭代器不能修改
//*lt += 20;
cout << *lt << endl; //这里的->返回的是指针类型呀,cout并没有指针类型的<<重载
lt++;
}
cout << endl;
/*for (auto& ch:v)
{
cout << ch << " ";
}
cout << endl;*/
}
}
🌈测试代码:
test.cpp:
cpp
#include"list.h"
namespace hwy_list
{
void test01()
{
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
auto it = l1.begin();
int k;
cin >> k;
while (k)
{
k--;
++it;
}
l1.insert(it, 9);
list<int>::iterator lt = l1.begin();
while (lt != l1.end())
{
cout << " " << *lt;
lt++;
}
cout << endl;
}
void test02()
{
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
auto it = l1.begin();
int k;
cin >> k;
while (k)
{
k--;
++it;
}
l1.erase(it);
for (auto ch : l1)
{
cout << " " << ch;
}
cout << endl;
}
void test03()
{
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
l1.push_back(99);
l1.push_front(66);
for (auto ch : l1)
{
cout << " " << ch;
}
cout << endl;
l1.pop_back();
l1.pop_front();
for (auto ch : l1)
{
cout << " " << ch;
}
cout << endl;
cout << l1.size() << endl;
}
void test04()
{
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
l1.push_back(99);
l1.push_front(66);
print_container(l1);
int c = l1.size();
cout << c << endl;
cout << l1.empty() << endl;
}
struct AA
{
AA(int a1 = 0, int a2 = 0)
:_a1(a1),
_a2(a2)
{
}
int _a1;
int _a2;
};
template<class T>
ostream& operator<<(ostream& os, const T& obj)
{
os << obj._a1 << obj._a2; // 自定义输出格式
return os;
}
void test05()
{
list<AA> l1;
l1.push_back(AA());
l1.push_back(AA());
l1.push_back(AA());
l1.push_back(AA());
auto it = l1.begin();
while (it != l1.end())
{
//cout << (*it)._a1 << ':' <<(*it)._a2<<endl ;
//cout << it.operator->()->_a1 << ':' << it.operator->()->_a2 << endl; 原本应该有两个->
//简写
cout << *it << endl; //这里就是对的
it++;
}
print_container(l1);//有问题
}
//迭代器失效问题
void test06()
{
list<int> l1;
l1.push_back(1);
l1.push_back(2);
l1.push_back(3);
l1.push_back(4);
l1.push_back(4);
l1.push_back(99);
l1.push_front(66);
//插入不会造成迭代器失效
//删除节点会,删除后就是野指针了
l1.clear();
print_container(l1);
}
void test07()
{
list<int> l2;
l2.push_back(1);
l2.push_back(2);
l2.push_back(3);
l2.push_back(4);
l2.push_back(5);
list<int> l1(l2);
print_container(l1); //这个里边对象没有_a1和_a2
print_container(l2);
}
void test08()
{
list<int> l2, l1;
l2.push_back(1);
l2.push_back(2);
l2.push_back(3);
l2.push_back(4);
l2.push_back(5);
l1 = l2;
print_container(l2);
print_container(l1);
}
}
int main()
{
hwy_list::test01();
hwy_list::test02();
hwy_list::test03();
hwy_list::test04();
hwy_list::test05();
hwy_list::test06();
hwy_list::test07();
hwy_list::test08();
return 0;
}
🎁结语:
今天的分享就到这里,感谢各位大佬的关注,还请大家多多支持哦!
