前面我们分析了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的实现,项目地址:https://github.com/zinx2016/MiniSTL/tree/master/MiniSTL
 
 

STL—list的更多相关文章

  1. 详细解说 STL 排序(Sort)

    0 前言: STL,为什么你必须掌握 对于程序员来说,数据结构是必修的一门课.从查找到排序,从链表到二叉树,几乎所有的算法和原理都需要理解,理解不了也要死记硬背下来.幸运的是这些理论都已经比较成熟,算 ...

  2. STL标准模板库(简介)

    标准模板库(STL,Standard Template Library)是C++标准库的重要组成部分,包含了诸多在计算机科学领域里所常见的基本数据结构和基本算法,为广大C++程序员提供了一个可扩展的应 ...

  3. STL的std::find和std::find_if

    std::find是用来查找容器元素算法,但是它只能查找容器元素为基本数据类型,如果想要查找类类型,应该使用find_if. 小例子: #include "stdafx.h" #i ...

  4. STL: unordered_map 自定义键值使用

    使用Windows下 RECT 类型做unordered_map 键值 1. Hash 函数 计算自定义类型的hash值. struct hash_RECT { size_t operator()(c ...

  5. C++ STL简述

    前言 最近要找工作,免不得要有一番笔试,今年好像突然就都流行在线笔试了,真是搞的我一塌糊涂.有的公司呢,不支持Python,Java我也不会,C有些数据结构又有些复杂,所以是时候把STL再看一遍了-不 ...

  6. codevs 1285 二叉查找树STL基本用法

    C++STL库的set就是一个二叉查找树,并且支持结构体. 在写结构体式的二叉查找树时,需要在结构体里面定义操作符 < ,因为需要比较. set经常会用到迭代器,这里说明一下迭代器:可以类似的把 ...

  7. STL bind1st bind2nd详解

    STL bind1st bind2nd详解   先不要被吓到,其实这两个配接器很简单.首先,他们都在头文件<functional>中定义.其次,bind就是绑定的意思,而1st就代表fir ...

  8. STL sort 函数实现详解

    作者:fengcc 原创作品 转载请注明出处 前几天阿里电话一面,被问到STL中sort函数的实现.以前没有仔细探究过,听人说是快速排序,于是回答说用快速排序实现的,但听电话另一端面试官的声音,感觉不 ...

  9. STL的使用

    Vector:不定长数组 Vector是C++里的不定长数组,相比传统数组vector主要更灵活,便于节省空间,邻接表的实现等.而且它在STL中时间效率也很高效:几乎与数组不相上下. #include ...

  10. [C/C++] C/C++延伸学习系列之STL及Boost库概述

    想要彻底搞懂C++是很难的,或许是不太现实的.但是不积硅步,无以至千里,所以抽时间来坚持学习一点,总结一点,多多锻炼几次,相信总有一天我们会变得"了解"C++. 1. C++标准库 ...

随机推荐

  1. 一天搞定CSS: CSS选择器优先级--08

    选择器优先级:是指代码的执行顺序 通俗的说,就是多个选择器同时对一个标签分别添加样式,那么这个标签显示那个选择器设置的样式 1.优先级规则 2.规则1和2说明 优先级相同,谁后谁优先 优先级不同,优先 ...

  2. Publishing failed with multiple errors.问题解决

    问题:Publishing failed with multiple errors.(发布失败与多个错误) 原因:项目工程文件删除,但eclipse里面仍显示存在. 解决方案:刷新项目工程,重新部署, ...

  3. STL语法——映射:map 反片语(Ananagrams,UVa 156)

    Description Most crossword puzzle fans are used to anagrams--groups of words with the same letters i ...

  4. js事件循环

    之前有看过一些事件循环的博客,不过一阵子没看就发现自己忘光了,所以决定来自己写一个博客总结下! 首先,我们来解释下事件循环是个什么东西: 就我们所知,浏览器的js是单线程的,也就是说,在同一时刻,最多 ...

  5. canvas水波纹效果

    先看效果 演示效果 自然界中水波纹效果十分麻烦,我这里只是根据水波纹的几个特性,在理想环境下模拟水波纹的扩散效果. 这里应用到的属性有:扩散.波动.折射. 扩散:很好理解,水波纹会从触发原点开始向周围 ...

  6. WKWebView 官方文档

    WKWebView 类 一个WKWebView对象可以显示交互式的web内容.就像一个应用程序的浏览器.你可以使用WKWebView类嵌入Web内容的应用程序.这样做,创造一个WKWebView对象, ...

  7. angularjs下拉框实现渲染html

    angualrjs处于安全的考虑,插值 指令会对相应字符串进行过滤,避免出现html攻击.但是在一些时候,我们需要渲染html,比如实现一个分级的下拉框,代码如下: <body ng-app=& ...

  8. POJ 2388

    还是水题,简单的排序.大半夜的,没脑子想太复杂的代码了,就随手找了段以前写的插入排序将就着用了. 题目的意思就是取一个数列的中位数,很简单,排序后取a[n/2]即可. 代码如下: #ifndef _2 ...

  9. django favicon配置

    其实网站加一个图标,在/static/images/里面放置favicon.ico 1. 直接url里修改 from django.views.generic.base import Redirect ...

  10. 2~62位任意进制转换(c++)

    进制转换的符号表为[0-9a-zA-Z],共61个字符,最大可表示62进制. 思路是原进制先转换为10进制,再转换到目标进制. 疑问: 对于负数,有小伙伴说可以直接将符号丢弃,按照整数进行进位转换,最 ...