STL—list
前面我们分析了vector,这篇介绍STL中另一个重要的容器list
list的设计
list由三部分构成:list节点、list迭代器、list本身
list节点
list是一个双向链表,所以其list节点中有前后两个指针。如下:
// list节点
template <typename T>
struct __list_node
{
typedef void* void_pointer;
void_pointer prev; // 指向前一个节点
void_pointer next; // 指向下一个节点
T data; // 节点的值
};
list迭代器
前面我们说过vector是利用其内存分配类型成员给vector分配一大块内存,而其迭代器是原始指针,所以其迭代器的移动就是指针的移动,vector那样通过指针的移动就能得到下一个元素,不需要特别设计。而list是链表结构,链表中每个节点的内存不连续,list的迭代器就是对外隐藏了从一个节点是如何移动到下一个节点的具体细节,使得外部只要将迭代器自增或自减就能得到相邻的节点。
list迭代器只有一个指向链表节点的指针数据成员。如下:
typedef __list_node<T>* link_type; link_type node; // point to __list_node
下面是迭代器的前置自增和前置自减运算符的源码,可以看到是通过节点的前向和后向指针来完成从一个节点移动到另一个节点:
self& operator++() { node = (link_type)(*node).next; return *this;}
self& operator--() { node = (link_type)(*node).prev; return *this;}
list
和vector一样,list也有个空间配置器的类型成员,通过该类型来为list的每个节点分配内存,并且通过该类型成员将外部指定的节点数目转换为相应节点所需的内存。所以list的内存模型是每个链表节点分配一块单独的内存,然后将每个节点连接起来。而vector的内存模型是分配一大块连续的内存。如下:
// 空间配置器
typedef simple_alloc<list_node, alloc> list_node_allocator;
实际上,list不仅是一个双向链表,而且还是一个环状的双向链表。为了设计的方便,在list中放置一个node指针,该指针指向一个空白节点,该空白节点的下一个节点是链表中起始节点,而链表的尾节点的下一个节点为该空白节点。虽然list底层是一个环状双向链表,但通过这样设计后对外就表现出一个普通的双向链表,符合一般习惯。这样设计还有很多好处,比如快速得到链表的首尾节点。如下。
private:
//指向空白节点
link_type node;
public:
// 通过空白节点node完成
iterator begin() const { return (link_type)(*node).next; }
iterator end() const { return node;}
bool empty() const { return node->next == node; }
下面我们看list内部是如何构造一个链表的。以我们对list的常用使用方法 list<int> lis(10)为例:
首先调用构造函数
explicit list(size_type n)
{
empty_initialize();
insert(begin(), n, T());
}
该构造函数会先调用empty_initialize()为list分配一个空白节点,并设置前向后向指针
void empty_initialize()
{
node = get_node();
node->next = node;
node->prev = node;
}
link_type get_node() { return list_node_allocator::allocate();}
然后构造函数会循环以插入相应个数的链表节点,每次插入时会分配一个节点大小的内存,然后对这块内存初始化,注意插入位置是在指定位置之前插入。由于list的内存模型和vector内存模型的区别,vector每次插入时由于可能会造成内存的重新配置,会造成原先所有的迭代器失效。而list的插入只是为新节点分配内存,并将其添加到链表中,对链表中其他节点的内存不会造成影响,所以list的插入则不会引起迭代器失效。如下。
template <typename T>
void
list<T>::insert(iterator position, size_type n, const T& x)
{
for (; n > ; --n)
insert(position, x);
}
template <typename T>
void
list<T>::insert(iterator position, const T& x)//posiiton之前插入
{
link_type tmp = create_node(x);
tmp->next = position.node;
tmp->prev = position.node->prev;
(link_type(position.node->prev))->next = tmp;
position.node->prev = tmp;
}
link_type create_node(const T& x)
{
link_type p = get_node();
construct(&p->data, x);
return p;
}
link_type get_node() { return list_node_allocator::allocate();}
(全文完)
STL—list的更多相关文章
- 详细解说 STL 排序(Sort)
0 前言: STL,为什么你必须掌握 对于程序员来说,数据结构是必修的一门课.从查找到排序,从链表到二叉树,几乎所有的算法和原理都需要理解,理解不了也要死记硬背下来.幸运的是这些理论都已经比较成熟,算 ...
- STL标准模板库(简介)
标准模板库(STL,Standard Template Library)是C++标准库的重要组成部分,包含了诸多在计算机科学领域里所常见的基本数据结构和基本算法,为广大C++程序员提供了一个可扩展的应 ...
- STL的std::find和std::find_if
std::find是用来查找容器元素算法,但是它只能查找容器元素为基本数据类型,如果想要查找类类型,应该使用find_if. 小例子: #include "stdafx.h" #i ...
- STL: unordered_map 自定义键值使用
使用Windows下 RECT 类型做unordered_map 键值 1. Hash 函数 计算自定义类型的hash值. struct hash_RECT { size_t operator()(c ...
- C++ STL简述
前言 最近要找工作,免不得要有一番笔试,今年好像突然就都流行在线笔试了,真是搞的我一塌糊涂.有的公司呢,不支持Python,Java我也不会,C有些数据结构又有些复杂,所以是时候把STL再看一遍了-不 ...
- codevs 1285 二叉查找树STL基本用法
C++STL库的set就是一个二叉查找树,并且支持结构体. 在写结构体式的二叉查找树时,需要在结构体里面定义操作符 < ,因为需要比较. set经常会用到迭代器,这里说明一下迭代器:可以类似的把 ...
- STL bind1st bind2nd详解
STL bind1st bind2nd详解 先不要被吓到,其实这两个配接器很简单.首先,他们都在头文件<functional>中定义.其次,bind就是绑定的意思,而1st就代表fir ...
- STL sort 函数实现详解
作者:fengcc 原创作品 转载请注明出处 前几天阿里电话一面,被问到STL中sort函数的实现.以前没有仔细探究过,听人说是快速排序,于是回答说用快速排序实现的,但听电话另一端面试官的声音,感觉不 ...
- STL的使用
Vector:不定长数组 Vector是C++里的不定长数组,相比传统数组vector主要更灵活,便于节省空间,邻接表的实现等.而且它在STL中时间效率也很高效:几乎与数组不相上下. #include ...
- [C/C++] C/C++延伸学习系列之STL及Boost库概述
想要彻底搞懂C++是很难的,或许是不太现实的.但是不积硅步,无以至千里,所以抽时间来坚持学习一点,总结一点,多多锻炼几次,相信总有一天我们会变得"了解"C++. 1. C++标准库 ...
随机推荐
- CI Weekly #20 | 从持续集成的角度看 “云” 的价值
很多移动开发工程师对 fastlane 耳熟能详,最近 flow.ci 的 iOS 工作流「编译」这步已采用 fastlane gym 工具(iOS 应用打包签名自动化),进一步优化了构建打包速度.快 ...
- JVM-2.Class文件结构
1.Class文件 (1)无关性:除了平台无关性,JVM还支持语言无关性:目前Clojure.Groovy.JRuby.Jyphon.Scala等语言可以在JVM上运行.实现语言无关性的原理仍然是字节 ...
- 创建的docker容器时间显示错误/date错误/时区错误
前几天在测试应用的功能时,发现存入数据库中的数据create_time或者update_time字段总是错误,其他数据都是正常的,只有关于时间的字段是错误的. 进入linux服务器中查看,也没有任何的 ...
- OpenGL ES 2.0 渲染管线 学习笔记
图中展示整个OpenGL ES 2.0可编程管线 图中Vertex Shader和Fragment Shader 是可编程管线: Vertex Array/Buffer objects 顶点数据来源, ...
- 写JS自执行函数时要注意的
JS是非强类型语言,且IDE也不够智能,所以经常会在语句结束时漏写了分号,一般情况下这是不影响运行的, 但如果后面跟上的是一段自执行的函数,就会报出 "..... is not functi ...
- java小白进阶安卓第一天
- 一致性Hash算法与代码实现
一致性Hash算法: 先构造一个长度为232的整数环(这个环被称为一致性Hash环),根据节点名称的Hash值(其分布为[0, 232-1])将服务器节点放置在这个Hash环上,然后根据数据的Key值 ...
- docker 汇总
整理中 ... 一. docker 基础 配置参数最佳实践 二. docker 编排工具 docker swarm mesos kubernetes AWS ECS 三. docker 生态 dock ...
- kaggle
注册kaggle可真所谓费劲心思,先是邮箱验证不来,换了两三个浏览器都不成功,非常恼火,没有验证码,最后还是FQ加谷歌浏览器,哎,注册之旅还是非常坎坷德,但是好消息是注册成功了.接下来是机器学习语言, ...
- R语言统计分析技术研究——岭回归技术的原理和应用
岭回归技术的原理和应用 作者马文敏 岭回归分析是一种专用于共线性分析的有偏估计回归方法,实质上是一种改良的最小二乘估计法,通过放弃最小二乘法的无偏性,以损失部分信息,降低精度为代价获得回归系数更为符合 ...