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++标准库 ...
随机推荐
- 关机和重启Linux命令
常用命令: shoutdown -h 10 十分钟后关机 shoutdown -r 10 十分钟重启 shoutdow -h now 立刻关机 shoutdow -r now 立刻重启 不安全的 ...
- 一天搞定CSS: 浮动(float)及文档流--10
浮动(float),一个我们即爱又恨的属性.爱,因为通过浮动,我们能很方便地布局: 恨,浮动之后遗留下来太多的问题需要解决,特别是IE6-7(以下无特殊说明均指 windows 平台的 IE浏览器). ...
- Java IO流之内存流
内存流 1)内存流主要用来操作内存 2)分类 ByteArrayInputStream 主要完成将内容从内存读入程序之中 ByteArrayOutputStream 主要是将数据写入到内存中. 3)输 ...
- 数据库并行读取和写入(Python实现)
这篇主要记录一下如何实现对数据库的并行运算来节省代码运行时间.语言是Python,其他语言思路一样. 前言 一共23w条数据,是之前通过自然语言分析处理过的数据,附一张截图: 要实现对news主体的读 ...
- String、StringBuffer、StringBuilder比较
String.StringBuffer.StringBuilder三者是字符串中重要的内容,也是面试过程中经常问到的问题,下面就来总结一下三者的区别. 1.三者都可以存储和操作字符串. 2.Strin ...
- Spring学习(1)----入门学习(附spring-framework下载地址)
(一)Spring是什么 Spring是一个开源框架,为了解决企业应用开发的复杂性而创建的,但现在已经不止应用于企业应用 是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架- 从大小和开销 ...
- 安装python2.7
系统的yum程序使用的是python2.6,不能够卸载系统所带的python环境.python2.7的安装命令如下 tar vjxf Python-2.7.1.tar.bz2 cd Python-2. ...
- RMAN备份与恢复(二)--常用操作学习
(1)连接目标数据库 在RMAN中可以建立与目标数据库或恢复目录数据库的连接.与目标数据库连接时,用户须具有sysdba系统权限,以保证可以进行数据库的备份.修复与恢复工作. 可以在操作系统命令提示符 ...
- java入门学习笔记之1(类的定义,代码的编译执行)
这篇文章讲解Java代码的基本执行过程 我们先抛开各种JAVA IDE,开发工具,只使用文本编辑器,以突出最本质的东西. 在Linux环境下,我们编辑一个文件: vim HelloWorld.java ...
- ORA-12638: 身份证明检索失败 解决方法
用PL/SQL或Navicat连接本地或远程Oracle数据库的时候报错:ORA-12638: 身份证明检索失败 解决方法: 开始 -> 所有程序 -> Oracle - Oracle_h ...