文章目录
- [一、 前言](#一、 前言)
- [二、 铺垫](#二、 铺垫)
- [三、 list的介绍](#三、 list的介绍)
- [四、 list的使用](#四、 list的使用)
-
- [1. 构造函数及拷贝构造函数](#1. 构造函数及拷贝构造函数)
- [2. 析构函数](#2. 析构函数)
- [3. 赋值运算符重载](#3. 赋值运算符重载)
- [4. 迭代器](#4. 迭代器)
- [5. capacity相关接口](#5. capacity相关接口)
- [6. 元素访问相关接口](#6. 元素访问相关接口)
- [7. 元素修改相关接口](#7. 元素修改相关接口)
- [8. 其他操作接口](#8. 其他操作接口)
- [五、 源代码](#五、 源代码)
一、 前言
之前我们已经模拟实现了vector,大家应该对模板也有了更深的理解,接下来我们就会来看看下一个stl容器------list的各种接口及应用---------点击查看《vector的实现》
二、 铺垫
这里我们对各种迭代器来分个类,因为list这里的迭代器不像string和vector那样是原生指针了。
按照功能分类
- iterator
- reverse_iterator
- const_iterator
- const_reverse_iterator
按照性质分类
- 单向迭代器:forward_list、unordered_map、unordered_set等
- 双向迭代器:list、map、set等
- 随机迭代器:vector、string、deque等
这里按性质分类的概念可能大家第一次见到,
我们这里的单向迭代器只支持++,
双向迭代器支持++/--,
随机迭代器支持++/--/+/-
这里做这个铺垫主要是因为不同的函数接口所支持的迭代器类型是不同的,迭代器之间是有种包容关系的,在C++中应该说是继承关系,这个我们后面再说,
这里还引入了两个不存在的迭代器,大家在查文档的时候应该会遇到,其实就是可以传入任意迭代器,支持单向迭代器的函数一定支持双向迭代器,反之则不支持了
三、 list的介绍

- list也是用模板来实现的,所以说使用的时候也是一定要显式实例化
- list其实就是我们在数据结构那里所学习的链表
四、 list的使用
注意:list使用时也是需要头文件的
1. 构造函数及拷贝构造函数

- list我们是可以默认不传参的,它会通过空间配置器来创建一个list(这里的空间配置器我们也是跟vector那里所说的一样不做过多叙述)
- 可以通过指定n个指定类型来创建list
- 可以通过迭代器来创建list,这个迭代器的类型可以是list的也可以是其他类型的,虽然我们list的迭代器底层并不是指针,但是依然可以传指针来进行构造的,所以我们在创建list时也可以传指针
- 拷贝构造传list类型的对象即可
cpp
#include<iostream>
#include<list>
using namespace std;
void test1()
{
list<int> lt1;
list<int> lt2(2, 1);
list<int> lt3(lt2.begin(), lt2.end());
int arr[] = { 1, 2, 3, 4 };
list<int> lt4(arr, arr + sizeof(arr) / sizeof(arr[0]));
list<int> lt5(lt4);
}
int main()
{
test1();
return 0;
}

2. 析构函数

- 析构函数我们了解一下就行,只是做资源清理的,后续我们在实现中会自己实现
3. 赋值运算符重载

- 这里再次强调与拷贝构造一定要区分开,如果被赋值的对象是存在的,那么就是赋值运算符重载,如果不存在则调用拷贝构造
- 利用赋值运算符重载可以让我们直接覆盖掉指定对象的原始内容,下面的调试过程中就可以看到lt5被lt2给覆盖了

4. 迭代器

- 这里分别是对应的正向迭代器和反向迭代器以及const对象对应的正向迭代器和反向迭代器
- 我们要记住迭代器是左闭右开的
- list的迭代器就不是指针了,后面在实现部分我们会详细介绍
- 由于不同类要使用的迭代器不同,所以迭代器iterator的使用要声明类域,即声明是哪一个类的迭代器
cpp
void test2()
{
list<int> lt(5, 1);
list<int>::iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
}
int main()
{
test2();
return 0;
}

5. capacity相关接口

- empty是用来判空的
- size返回list类模板实例化的对象中的有效数据个数
- max_size返回list能容纳的最大元素数,基本不用
cpp
void test3()
{
list<int> lt(5, 1);
cout << lt.empty() << endl;
cout << lt.size() << endl;
cout << lt.max_size() << endl;
}
int main()
{
test3();
return 0;
}

6. 元素访问相关接口

- front返回第一个有效元素的引用
- back返回最后一个有效元素的引用
cpp
void test4()
{
list<int> lt(3, 2);
lt.front() = 1;
lt.back() = 3;
cout << lt.front() << " " << lt.back();
}
int main()
{
test4();
return 0;
}

7. 元素修改相关接口

- assign是一种赋值接口,可以用n个val值来赋值,也可以使用迭代器区间来赋值,会将要传入数据的对象的原有的数据全部清空,再去分派数据
- emplace系列我们先不讨论
- push_front用来头插一个数据,pop_front用来头删一个数据
- push_back用来尾插一个数据,pop_back用来尾删一个数据
- insert在list中和在vector中不一样,list中不涉及迭代器失效,我们在实现那里会具体讨论,可以在指定位置前插入一个指定值,也可以在指定位置前插入n个相同的值,甚至可以在指定位置前插入一段迭代器区间
- erase可以删除指定位置的数据,也可以删除一段迭代器区间,erase这里则会涉及迭代器失效
- swap可以交换两个list的数据,效率会比库中好
- resize用来调整list的大小,如果指定大小小于当前大小,则会将多余部分直接删除,如果指定大小大于当前大小,则会在后续补充指定的val值,如果没有指定val则会调用当前类型的默认构造函数
- clear用于清空指定list的数据
cpp
void test5()
{
list<int> lt(3, 1);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.assign(5, 2);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.push_front(6);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.pop_front();
for (auto e : lt) cout << e << " ";
cout << endl;
lt.push_back(6);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.pop_back();
for (auto e : lt) cout << e << " ";
cout << endl;
list<int>::iterator it = lt.begin();
for (int i = 0; i < 2; i++) it++;
lt.insert(it, 9);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.insert(it, 2, 7);
for (auto e : lt) cout << e << " ";
cout << endl;
list<int> _lt(2, 4);
lt.insert(it, _lt.begin(), _lt.end());
for (auto e : lt) cout << e << " ";
cout << endl;
lt.erase(--lt.end());
//由于这里要删除的是最后一个位置的数据,
//end返回的是最后一个数据的下一个位置的迭代器
//所以要将--end使迭代器指向最后一个位置的数据
for (auto e : lt) cout << e << " ";
cout << endl;
lt.erase(++lt.begin(), lt.end());
for (auto e : lt) cout << e << " ";
cout << endl;
lt.swap(_lt);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.resize(5, 6);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.clear();
for (auto e : lt) cout << e << " ";
cout << endl;
}
int main()
{
test5();
return 0;
}

8. 其他操作接口

- splice接口有种剪切的感觉,它有三种传参方式,第一种传一个指定位置和一个list对象,可以让传入的list对象内所有的数据都转移到调用的list对象的指定位置之前,第二种传参是在传一个指定位置和一个list的对象之后再传入一个指定位置。就可以在调用splice的list对象的指定位置之前插入一个传入的list对象的指定位置的值,第三种传参就是把第二种传参方式中的指定位置换成指定的迭代器区间,当热插入的也是一段迭代器区间,我们后面演示大家就清晰了
- remove移除list中的节点的数据为val的所有节点,remove_if我们这里不做讨论
- unique是进行去重,但是我们需要注意它只会在相邻元素之间去重,所以说最好是在排好序的list中去调用去重
- merge将一个list对象x的数据归并到另一个list对象中去,这个对象x的数据节点会被全部链接到另一个list对象中,所以调用完之后对象x的有效数据个数为0,进行归并的前提是两个对象的数据是有序的,所以两个对象都应该提前调用sort确保数据是有序的
- 在我们前面铺垫那里介绍了迭代器按性质分类,我们可以在算法库中的sort里发现我们的list并不适用算法库中的sort,所以list这里单独自己实现了一个sort但是效率很低,如果数据量比较小还可以调用,如果数据量大的话不如先把list中的数据转移到vector中调用算法库中的sort排序后在传回给list
- reverse是对list的数据进行逆置,其实这个有点多余,在STL中的算法中同样也有传入迭代器区间进行逆置的reverse,两者的功效都是相同的
cpp
void test6()
{
list<int> lt(3, 1);
list<int> _lt(3, 2);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.splice(lt.begin(), _lt);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.splice(lt.begin(), lt, --lt.end());
for (auto e : lt) cout << e << " ";
cout << endl;
lt.splice(lt.begin(), lt, ++lt.begin(), --lt.end());
for (auto e : lt) cout << e << " ";
cout << endl;
lt.sort();
for (auto e : lt) cout << e << " ";
cout << endl;
lt.remove(2);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.push_back(3);
lt.unique();
for (auto e : lt) cout << e << " ";
cout << endl;
_lt.assign(3, 4);
lt.merge(_lt);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.reverse();
for (auto e : lt) cout << e << " ";
cout << endl;
reverse(lt.begin(), lt.end());
for (auto e : lt) cout << e << " ";
cout << endl;
}
int main()
{
test6();
return 0;
}

五、 源代码
cpp
#include<iostream>
#include<algorithm>
#include<list>
using namespace std;
void test1()
{
list<int> lt1;
list<int> lt2(2, 1);
list<int> lt3(lt2.begin(), lt2.end());
int arr[] = { 1, 2, 3, 4 };
list<int> lt4(arr, arr + sizeof(arr) / sizeof(arr[0]));
list<int> lt5(lt4);
lt5 = lt2;
}
void test2()
{
list<int> lt(5, 1);
list<int>::iterator it = lt.begin();
while (it != lt.end())
{
cout << *it << " ";
++it;
}
cout << endl;
for (auto e : lt)
{
cout << e << " ";
}
cout << endl;
}
void test3()
{
list<int> lt(5, 1);
cout << lt.empty() << endl;
cout << lt.size() << endl;
cout << lt.max_size() << endl;
}
void test4()
{
list<int> lt(3, 2);
lt.front() = 1;
lt.back() = 3;
cout << lt.front() << " " << lt.back();
}
void test5()
{
list<int> lt(3, 1);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.assign(5, 2);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.push_front(6);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.pop_front();
for (auto e : lt) cout << e << " ";
cout << endl;
lt.push_back(6);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.pop_back();
for (auto e : lt) cout << e << " ";
cout << endl;
list<int>::iterator it = lt.begin();
for (int i = 0; i < 2; i++) it++;
lt.insert(it, 9);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.insert(it, 2, 7);
for (auto e : lt) cout << e << " ";
cout << endl;
list<int> _lt(2, 4);
lt.insert(it, _lt.begin(), _lt.end());
for (auto e : lt) cout << e << " ";
cout << endl;
lt.erase(--lt.end());
//由于这里要删除的是最后一个位置的数据,
//end返回的是最后一个数据的下一个位置的迭代器
//所以要将--end使迭代器指向最后一个位置的数据
for (auto e : lt) cout << e << " ";
cout << endl;
lt.erase(++lt.begin(), lt.end());
for (auto e : lt) cout << e << " ";
cout << endl;
lt.swap(_lt);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.resize(5, 6);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.clear();
for (auto e : lt) cout << e << " ";
cout << endl;
}
void test6()
{
list<int> lt(3, 1);
list<int> _lt(3, 2);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.splice(lt.begin(), _lt);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.splice(lt.begin(), lt, --lt.end());
for (auto e : lt) cout << e << " ";
cout << endl;
lt.splice(lt.begin(), lt, ++lt.begin(), --lt.end());
for (auto e : lt) cout << e << " ";
cout << endl;
lt.sort();
for (auto e : lt) cout << e << " ";
cout << endl;
lt.remove(2);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.push_back(3);
lt.unique();
for (auto e : lt) cout << e << " ";
cout << endl;
_lt.assign(3, 4);
lt.merge(_lt);
for (auto e : lt) cout << e << " ";
cout << endl;
lt.reverse();
for (auto e : lt) cout << e << " ";
cout << endl;
reverse(lt.begin(), lt.end());
for (auto e : lt) cout << e << " ";
cout << endl;
}
int main()
{
test6();
return 0;
}
