菜鸟笔记
提升您的技术认知

一篇文章搞懂STL中的顺序容器之list

list是什么?    list本质上就是双向链表

相对与上一篇所讲的vector,我们知道双向链表有它自己的优点,首先空间利用比较灵活,所以省空间.
而且插入删除元素都是常数时间
下面将参照list的源码,将其分为下面几部分去讲

1.结点

既然list是链表,那就必须得定义它自己的结点类
在源码中它的结点是这样子的 

 

2.迭代器

不像vector是连续的内存空间,所以可以直接用指针作为迭代器
在 list中由于是链式存储的,所以得定义一个属于自己的迭代器 
直接看一些它的源码



可以看到在迭代器类中 有一个成员变量 node,用来存放该迭代器指向的结点,
可以通过构造函数给node指定指向的结点,
也可以给通过复制构造函数给node指定好另一个迭代器的node指向的结点.
由于list只支持双向迭代器(因为双向链表要支持前移 后移),所以对运算符++ ,-- ,* ,->进行了重载.
注意 在list中插入结点和删除某个结点并不会使其他结点的迭代器失效,
而vector就并非如此,因为插入元素可能导致原有内存重新分配 导致原有迭代器失效

3.数据结构

list是一个双向循环链表,既然是循环,也就是说尾结点的下一个就是头结点,头结点的上一个就是尾结点.
同时只要用一个结点指针指向了其中一个结点,就可以遍历到其他任意结点
在list中定义了一个空白的尾结点指针 用node表示  如下图表示

通过这个node,就可以表示出其他任意一个节点了,这样就可以实现我们的一些list的基本操作, 

比如返回头结点(即返回node->next就可以了),返回尾结点(返回node本身就行了),返回头结点值,返回尾结点值,以及判空,返回长度

我们看一下在源码中是怎么实现的 



4.内存管理

4.1内存分配与释放

  • 空间配置器list_node_allocator
     

list默认使用alloc作为空间配置器, 但是为了更方便的以结点大小为配置单位,
依据alloc定义了list_node_allocator作为专属空间配置器,每次配置一个节点大小

list_node_allocator(n)就表示配置n个结点空间大小
以该空间配置器为基础,主要通过下面四个函数来实现内存的分配与释放 

 

  • 开辟一个节点空间get_node()

  • 释放一个结点空间put_node()

  • 开辟一个结点空间并构造create_node()

  • 释放一个结点空间并析构destory_node()       

 

4.2内存使用

  • 构造函数list()

  • push_back()

  • push_front()

  • erase()

  • pop_front()

  • pop_back()

  • clear

  • remove

  • unique

  • splice

  • merge

  • reverse

  • sort

5.使用

6.其他问题

listqueue与vector之间的区别
list不再能够像vector一样以普通指针作为迭代器,因为其节点不保证在存储空间中连续存在;
list插入操作和结合才做都不会造成原有的list迭代器失效;
list不仅是一个双向链表,而且还是一个环状双向链表,所以它只需要一个指针;
list不像vector那样有可能在空间不足时做重新配置、数据移动的操作,所以插入前的所有迭代器在插入操作之后都仍然有效;deque是一种双向开口的连续线性空间,所谓双向开口,意思是可以在头尾两端分别做元素的插入和删除操作;可以在头尾两端分别做元素的插入和删除操作;
deque和vector最大的差异,一在于deque允许常数时间内对起头端进行元素的插入或移除操作,二在于deque没有所谓容量概念,因为它是动态地以分段连续空间组合而成,随时可以增加一段新的空间并链接起来,deque没有所谓的空间保留功能。