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提供的配 ...
随机推荐
- 32、Flask实战第32天:优化json数据的返回
接着上节,我们通过jsonify返回json数据非常方便 ... return jsonify({"code": 400, "message": message ...
- luogu P1979 华容道
solution 被假hash可了半天....sadQAQ code // luogu-judger-enable-o2 #include<queue> #include<cstdi ...
- JZYZOJ1372 [noi2002]荒岛野人 扩展欧几里得
http://172.20.6.3/Problem_Show.asp?id=1372 想法其实很好想,但是我扩展欧几里得还是用得不熟练,几乎是硬套模板,大概因为今天一个下午状态都不大好.扩展欧几里得算 ...
- HDU 6041 I Curse Myself(二分+搜索)
[题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=6041 [题目大意] 给出一个仙人掌图,求第k小生成树 [题解] 首先找到仙人掌图上的环,现在的问题 ...
- 【线段树区间合并】BZOJ1593-[Usaco2008 Feb]Hotel 旅馆
好无聊,以前写过没什么好讲的,水过.戳 #include<iostream> #include<cstdio> #include<cstdlib> #define ...
- 将int型数值拆分成4字节
- [典型漏洞分享]结合YS业务分析使用oauth协议的风险
结合YS业务分析oauth协议风险 问题描述: YS 使用QQ互联的openAPI实现QQ登录YS的功能,使用该功能需要在腾讯注册登录时的回调地址,根据oauth协议,用户的code或者access_ ...
- nio教程二
要使用Selector,得向Selector注册Channel,然后调用它的select()方法.这个方法会一直阻塞到某个注册的通道有事件就绪.一旦这个方法返回,线程就可以处理这些事件,事件的例子有如 ...
- maven的几个重要配置文件pom.xml、settings.xml;Maven打包生成包含所有依赖的jar包
一个java项目通过maven自动下载依赖时,会涉级读取三个配置文件,分别是项目下的pom.xml 文件 .用户家目录下的.m2/settings.xml 与 maven 全局配置settings.x ...
- html之marquee会移动的文字
该标签不是HTML3.2的一部分,并且只支持MSIE3以后内核,所以如果你使用非IE内核浏览器(如:Netscape)可能无法看到下面一些很有意思的效果 该标签是个容器标签 语法: <marqu ...