STL源码剖析(list)
SGI STL中list是使用环状双向链表实现的。它的结点结构定义如下:
template <class T>
struct __list_node {
typedef void* void_pointer;
void_pointer next;
void_pointer prev;
T data;
};
list定义了属于自己的迭代器,并重载了operator*(用于取结点的data成员)、operator+(用于取next结点)等等。
template<class T, class Ref, class Ptr>
struct __list_iterator {
// 定义相应型别
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, Ref, Ptr> self; typedef bidirectional_iterator_tag iterator_category;
typedef T value_type;
typedef Ptr pointer;
typedef Ref reference;
typedef __list_node<T>* link_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type; // 拥有一个指向对应结点的指针
link_type node; // 构造函数
__list_iterator() {}
__list_iterator(link_type x) : node(x) {}
__list_iterator(const iterator& x) : node(x.node) {} // 重载了iterator必须的操作符
reference operator*() const { return (*node).data; }
pointer operator->() const { return &(operator*()); }
self& operator++() {
node = (link_type)((*node).next);
return *this;
}
self& operator--() {
node = (link_type)((*node).prev);
return *this;
}
// ...
};
list的定义比较简单,而且环状双向链表的操作并不用过多的考虑边界条件。
list创建的时候会创建一个空白的结点,并用其node成员指向它,下面是list的基本定义:
template <class T, class Alloc = alloc>
class list {
protected:
typedef void* void_pointer;
typedef __list_node<T> list_node;
typedef simple_alloc<list_node, Alloc> list_node_allocator;
public:
typedef T value_type;
typedef value_type* pointer;
typedef value_type& reference;
typedef list_node* link_type;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
public:
// 定义迭代器类型
typedef __list_iterator<T, T&, T*> iterator;
protected:
link_type node; // 空白结点 链表尾结点
// ...
};
下面是list的示意图

根据示意图我们很容易理解list的构造:
template <class T, class Alloc = alloc>
class list {
public:
// ...
// 可以从图中直观的看出来
iterator begin() { return (link_type)((*node).next); }
iterator end() { return node; } // 默认构造函数
list() { empty_initialize(); }
protected:
// 为结点分配内存
link_type get_node() { return list_node_allocator::allocate(); }
// 回收内存
void put_node(link_type p) { list_node_allocator::deallocate(p); }
// 构造node
link_type create_node(const T& x) {
link_type p = get_node();
construct(&p->data, x);
return p;
}
// 销毁node
void destroy_node(link_type p) {
destroy(&p->data);
put_node(p);
}
// 初始化
void empty_initialize() {
node = get_node();
node->next = node;
node->prev = node;
}
// ...
};
list成员函数的实现其实就是对环状双向链表的操作。
首先是insert、erase、transfer的实现,关于插入删除大部分都调用这三个函数,实际上就是改变结点pre跟next指针的指向。
iterator insert(iterator position, const T& x) {
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;
return tmp;
}
iterator erase(iterator position) {
// 改变四个指针的指向 实际就是双向链表的元素删除
link_type next_node = link_type(position.node->next);
link_type prev_node = link_type(position.node->prev);
prev_node->next = next_node;
next_node->prev = prev_node;
destroy_node(position.node);
return iterator(next_node);
}
// 将[first, last)插入到position位置(可以是同一个链表)
void transfer(iterator position, iterator first, iterator last) {
if (position != last) {
// 实际上也是改变双向链表结点指针的指向 具体操作看下图
(*(link_type((*last.node).prev))).next = position.node;
(*(link_type((*first.node).prev))).next = last.node;
(*(link_type((*position.node).prev))).next = first.node;
link_type tmp = link_type((*position.node).prev);
(*position.node).prev = (*last.node).prev;
(*last.node).prev = (*first.node).prev;
(*first.node).prev = tmp;
}
}

有了上面3个函数,list对外的接口实现就非常简单了
void push_front(const T& x) { insert(begin(), x); }
void push_back(const T& x) { insert(end(), x); }
void pop_front() { erase(begin()); }
void pop_back() {
iterator tmp = end();
erase(--tmp);
}
// splice有很多重载版本
void splice(iterator position, list&, iterator first, iterator last) {
if (first != last)
transfer(position, first, last);
}
// merge函数实现跟归并排序中合并的操作类似
template <class T, class Alloc>
void list<T, Alloc>::merge(list<T, Alloc>& x) { ... }
// reserse函数每次都调用transfer将结点插入到begin()之前
template <class T, class Alloc>
void list<T, Alloc>::reverse() {
if (node->next == node || link_type(node->next)->next == node) return;
iterator first = begin();
++first;
while (first != end()) {
iterator old = first;
++first;
transfer(begin(), old, first);
}
}
// list必须使用自己的sort()成员函数 因为STL算法中的sort()只接受RamdonAccessIterator
// 该函数采用的是quick sort
template <class T, class Alloc>
void list<T, Alloc>::sort() { ... }
STL源码剖析(list)的更多相关文章
- STL"源码"剖析-重点知识总结
STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略多 :) 1.STL概述 STL提供六大组件,彼此可以组合 ...
- 【转载】STL"源码"剖析-重点知识总结
原文:STL"源码"剖析-重点知识总结 STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点 ...
- (原创滴~)STL源码剖析读书总结1——GP和内存管理
读完侯捷先生的<STL源码剖析>,感觉真如他本人所说的"庖丁解牛,恢恢乎游刃有余",STL底层的实现一览无余,给人一种自己的C++水平又提升了一个level的幻觉,呵呵 ...
- 《STL源码剖析》环境配置
首先,去侯捷网站下载相关文档:http://jjhou.boolan.com/jjwbooks-tass.htm. 这本书采用的是Cygnus C++ 2.91 for windows.下载地址:ht ...
- STL源码剖析读书笔记之vector
STL源码剖析读书笔记之vector 1.vector概述 vector是一种序列式容器,我的理解是vector就像数组.但是数组有一个很大的问题就是当我们分配 一个一定大小的数组的时候,起初也许我们 ...
- STL源码剖析 迭代器(iterator)概念与编程技法(三)
1 STL迭代器原理 1.1 迭代器(iterator)是一中检查容器内元素并遍历元素的数据类型,STL设计的精髓在于,把容器(Containers)和算法(Algorithms)分开,而迭代器(i ...
- STL"源码"剖析
STL"源码"剖析-重点知识总结 STL是C++重要的组件之一,大学时看过<STL源码剖析>这本书,这几天复习了一下,总结出以下LZ认为比较重要的知识点,内容有点略 ...
- 《STL源码剖析》相关面试题总结
原文链接:http://www.cnblogs.com/raichen/p/5817158.html 一.STL简介 STL提供六大组件,彼此可以组合套用: 容器容器就是各种数据结构,我就不多说,看看 ...
- STL源码剖析之序列式容器
最近由于找工作需要,准备深入学习一下STL源码,我看的是侯捷所著的<STL源码剖析>.之所以看这本书主要是由于我过去曾经接触过一些台湾人,我一直觉得台湾人非常不错(这里不涉及任何政治,仅限 ...
- STL源码剖析 — 空间配置器(allocator)
前言 以STL的实现角度而言,第一个需要介绍的就是空间配置器,因为整个STL的操作对象都存放在容器之中. 你完全可以实现一个直接向硬件存取空间的allocator. 下面介绍的是SGI STL提供的配 ...
随机推荐
- 第5天:Ansible-Playbook
Ansible中的每个模块专注于某一方面的功能.虽然每个模块实现的功能都比较简单,但是,将各个模块结合起来就可以实现比较复杂的功能.在Ansible中,将各个模块组合起来的文件是一个YAML格式的配置 ...
- UGUI的优点新UI系统三效率高效果好
UGUI的优点新UI系统三效率高效果好 通过对批处理(batching).纹理图集(texture atlasing)和新的canvas组件的支持,新UI系统提供了一个经过优化的解决方案,使得开发者添 ...
- Java 对象池实现
http://blog.csdn.net/bryantd/article/details/1100019 http://www.cnblogs.com/devinzhang/archive/2012/ ...
- windows上同时安装两个版本的mysql数据库
一.先停止之前安装的低版本mysql服务 二.将其他电脑上安装好的mysql拷贝过来 三.拷贝过来之后,进入该文件夹,删除掉data目录,然后打开my.ini,进行修改端口号,端口号改为3307,ba ...
- POJ 3378 Crazy Thairs(树状数组+DP)
[题目链接] http://poj.org/problem?id=3378 [题目大意] 给出一个序列,求序列中长度等于5的LIS数量. [题解] 我们发现对于每个数长度为k的LIS有dp[k][i] ...
- [LOJ2541]猎人杀
好题== 先转化一下,每个人被杀死后给他打标记,以后随机杀人时选到有标记的人就继续随机,这样并不会改变每个人被杀死的概率 直接算很难算,考虑容斥,我们枚举$S$表示在$1$死后才死的人至少在集合$S$ ...
- 【最大流】POJ3236-ACM Computer Factory
[题意] 装配一个电脑需要P个零件,现在给出N机器的信息,每个机器可以将k个电脑由状态{S1,S2..,Sp}转变为{Q1,Q2..,Qp},问最多能装配多少台电脑以及对应的方案? [思路] 1A.. ...
- 【线段树】HDU1754-I hate it
单点修改,区间最值的标程,没什么好说的. #include<iostream> #include<cstdio> #include<cstring> #includ ...
- Web安全测试指南--文件系统
上传: 编号 Web_FileSys_01 用例名称 上传功能测试 用例描述 测试上传功能是否对上传的文件类型做限制. 严重级别 高 前置条件 1. 目标web应用可访问,业务正常运行. 2. 目 ...
- dns问题,QQ打得开,网页打不开
dns问题,QQ打得开,网页打不开 ip4 dns 改为114.114.114.114. 原因有可能是路由出错之类的.114是默认的通用ip