【C++进阶】STL容器--list使用&&迭代器问题分析

目录

前言

[1. list的基本使用](#1. list的基本使用)

[1.1 list构造函数](#1.1 list构造函数)

[1.2 list迭代器](#1.2 list迭代器)

[1.3 list capacity](#1.3 list capacity)

[1.4 list元素访问](#1.4 list元素访问)

[1.5 list 修改操作](#1.5 list 修改操作)

insert

erase

swap

resize

clear

[2. list失效迭代器问题](#2. list失效迭代器问题)

[3. list使用算法库函数问题](#3. list使用算法库函数问题)

总结


前言

list(链表)在C++中非常重要的容器,它本质是一个带头双向循环链表。本文的主要内容是介绍list的基本使用以及对list迭代器问题。

1. list的基本使用

学习一种新的容器,首先我们需要先了解它的接口;

在C++的STL标准库中,list是一个带头双向循环链表;

list和vector不同,它不支持[ ]下标随机访问;

list是一个双向链表,它的元素在内存中不是连续存储的,因此并不需要像vector一样预留容量或者调整容量

1.1 list构造函数

C++98中默认有4中构造函数:

有四种构造方法:

  • 默认无参构造
cpp 复制代码
list<int> lt1;//无参构造
  • n个val构造
cpp 复制代码
list<int> lt2(10,0); // 10个0初始化
  • 迭代器区间构造
cpp 复制代码
vector<int> v{1,2,3,4,5};
list<int> lt3(v.begin(),v.end());
  • 拷贝构造
cpp 复制代码
list<int> lt4(10,0);
list<int> lt5(lt4);

除此之外还有比较常用的初始值设定项列表构造;

  • initializer list构造(初始值设定项列表构造)
cpp 复制代码
list<int> lt = {1, 2, 3, 4, 5};

1.2 list迭代器

list迭代器本质并不是指针,为了方便理解,可暂时将迭代器理解成一个指针,该指针指向list中的某个节点;

这4个迭代器接口大家都很熟悉,但需要注意的是list中它们的指向;

  • list迭代器遍历
cpp 复制代码
list<int> lt = { 1, 2, 3, 4, 5 };
list<int>::iterator it = lt.begin();
while (lt.end() != it)
{
	cout << *it << ' ';
	it++;
}
  • 范围for遍历
cpp 复制代码
for (auto e : lt)
{
	cout << e << ' ';
}

范围for循环确实是本质上是迭代器遍历,在执行时会被替换成迭代器;

注意:

在使用list迭代器时,我们往往使用的是it!=l.end(),而不是it<l.end(),因为list空间并不连续,每个节点的地址都不同(无法确认大小关系),在string和vector中,空间是连续的,可以使用;

1.3 list capacity

使用操作较为简单,使用list对象即可调用:

cpp 复制代码
lt.empty();
lt.size();

1.4 list元素访问

使用操作较为简单,使用list对象即可调用:

cpp 复制代码
lt.front();
lt.back();

1.5 list 修改操作

这里我们可以参考cplusplus上边的接口;

这里我就介绍一些比较常用的,其他的接口使用可以去查阅;

头插、头删、尾插、尾删我就不再详细介绍;注意这些操作只能插入删除一个元素!

assign接口功能是新元素替换列表中的内容,它的功能我们完全可以使用list的构造函数替代,这里不再详细介绍;

insert
  • 使用单个值插入
cpp 复制代码
list<int> mylist = {1, 2, 3, 4, 5};
auto it = mylist.begin();
it++; // 将迭代器it移动到第二个位置
mylist.insert(it, 10); // 在it位置插入值为10的元素
  • 使用相同值填充插入
cpp 复制代码
list<int> mylist = {1, 2, 3, 4, 5};
auto it = mylist.begin();
it++; // 将迭代器it移动到第二个位置
mylist.insert(it, 3, 10); // 在it位置插入3个值为10的元素
  • 迭代器区间插入
cpp 复制代码
list<int> mylist = {1, 2, 3, 4, 5};
auto it = mylist.begin();
it++; // 将迭代器it移动到第二个位置
vector<int> vec = {7, 8, 9};
mylist.insert(it, vec.begin(), vec.end()); // 在it位置插入vector中的元素
erase
  • 删除单个元素
cpp 复制代码
list<int> mylist = {1, 2, 3, 4, 5};
auto it = mylist.begin();
it++; // 将迭代器it移动到第二个位置
mylist.erase(it); // 删除it位置的元素
  • 删除范围内的元素:
cpp 复制代码
list<int> mylist = {1, 2, 3, 4, 5};
auto it1 = mylist.begin();
auto it2 = mylist.begin();
advance(it1, 1); // 将迭代器it1移动到第二个位置
advance(it2, 3); // 将迭代器it2移动到第四个位置
mylist.erase(it1, it2); // 删除从it1到it2范围内的元素

注意list迭代器不支持使用+或-的操作来进行移动 ;所以这里使用advance函数来移动迭代器;

swap

swap函数交换两个list的内容:

cpp 复制代码
list<int> list1 = {1, 2, 3};
list<int> list2 = {4, 5, 6};
list1.swap(list2); // 交换list1和list2的内容
resize

resize函数调整list的大小:

cpp 复制代码
list<int> mylist = {1, 2, 3, 4, 5};
mylist.resize(3); // 将mylist的大小调整为3,删除多余的元素
mylist.resize(5, 100); // 将mylist的大小调整为5,多出的元素用100填充

list没有reserve接口

clear

clear函数清空list的内容:

cpp 复制代码
list<int> mylist = {1, 2, 3, 4, 5};
mylist.clear(); // 清空mylist的内容,mylist现在为空

2. list失效迭代器问题

前面说过,此处大家可将迭代器暂时理解成类似于指针,迭代器失效即迭代器所指向的节点的无效,list的底层结构为带头结点的双向循环链表,在list中进行插入时是不会导致list的迭代
器失效;

list迭代器失效只会在erase删除操作时发生

删除节点后,节点的数据就会被释放,再使用迭代器访问这个节点,此时节点已经失效;

前边我们也提到过,可以通过返回值的方法来避免迭代器的非法访问;

节点被删除后,自动返回下一个位置的迭代器;

3. list使用算法库函数问题

如果要对链表进行排序呢?

其实list自己封装的有sort接口,list链表的sort函数使用的是归并排序算法;那它为什么不用算法库里的sort;

原因很简单,其实就是迭代器不兼容;

算法库里的sort它的参数是RandomAccessIterator,也就是随机访问迭代器;

这里就要补充一点知识,从不同容器的迭代器出发,容器不同迭代器实现也不同,那么迭代器的特性也不同;

根据迭代器的特性大致可以先分为三类:

  • 单向迭代器(Forward Iterator)
  • 双向迭代器(Bidirectional Iterator)
  • 随机访问迭代器(Random Access Iterator)

单向迭代器: 迭代器仅++操作,单链表

双向迭代器:迭代器支持++、--操作,双向链表

随机访问迭代器:迭代器支持++/--/+/-操作,vector、string

怎么分辨是否可以使用算法接口?

单向迭代支持++操作,满足所有迭代器的操作,三种类型的迭代器类型接口函数都可以使用;

双向迭代器支持++/--操作,满足随机访问迭代器的操作,那它就可以使用参数类型为双向迭代器类型的接口函数,参数类型为随机访问迭代器类型的接口;

它就有向上兼容的特性;

总结:

  • 单向迭代器可以使用三种迭代器类型的接口;
  • 双向迭代器可以使用:双向迭代器、随机迭代器类型的接口;
  • 随机迭代器仅可以使用随机迭代器类型的接口;

虽然list自己实现的有sort接口,但还是不建议使用,它的效率非常的低;

它甚至还没有这样操作的速度快:

将list数据拷贝到vector中排序,然后将排序好的数据拷贝回list;


总结

本文主要介绍的是list的一些基本使用,以及list迭代器问题和list使用算法库函数的问题。list的接口函数不多,但是在使用算法库函数时需要注意迭代器的类型;以上便是本期的全部内容,希望对你有所帮助,感谢阅读!

相关推荐
erxij3 分钟前
【游戏引擎之路】登神长阶(十四)——OpenGL教程:士别三日,当刮目相看
c++·经验分享·游戏·3d·游戏引擎
林开落L34 分钟前
前缀和算法习题篇(上)
c++·算法·leetcode
Prejudices1 小时前
C++如何调用Python脚本
开发语言·c++·python
单音GG1 小时前
推荐一个基于协程的C++(lua)游戏服务器
服务器·c++·游戏·lua
qing_0406031 小时前
C++——多态
开发语言·c++·多态
孙同学_1 小时前
【C++】—掌握STL vector 类:“Vector简介:动态数组的高效应用”
开发语言·c++
charlie1145141912 小时前
Qt Event事件系统小探2
c++·qt·拖放·事件系统
iiiiiankor2 小时前
C/C++内存管理 | new的机制 | 重载自己的operator new
java·c语言·c++
小辛学西嘎嘎2 小时前
C/C++精品项目之图床共享云存储(3):网络缓冲区类和main
c语言·开发语言·c++
c语言鹌鹑蛋2 小时前
C++初阶 --- 类和对象(1)
开发语言·c++