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提供的配 ...
随机推荐
- Xamarin iOS项目找不到模拟器
Xamarin iOS项目找不到模拟器 在Visual Studio中,突然找不到模拟器,并且项目提示试用期过期:Trial period has expired.出现这种情况,是由于苹果系统中Xam ...
- 初见Python<5>:条件、循环和其他语句
1.使用逗号输出 使用逗号隔开,可以打印多个表达式.打印后,各项之间自动以一个空格隔开. 也可以同时输出文本和变量值. 可以和字符串连接符“+”一起使用. 2.从模块中导入函数 从模块导入函数的方 ...
- [BZOJ4832]抵制克苏恩(概率期望DP)
方法一:倒推,最常规的期望DP.f[i][a][b][c]表示还要再攻击k次,目前三种随从个数分别为a,b,c的期望攻击英雄次数,直接转移即可. #include<cstdio> #inc ...
- [AGC026B]rng_10s
题意:一个商店,第一天早上有$A$罐苹果汁,一个人每天上午都会来买$B$罐,每天晚上如果剩余罐数$\leq C$那么会增加$D$罐,问能否无限买 收集一个套路...? 首先如果$B\gt A$或者$B ...
- ZXing for Android 修改为竖屏模式
zxing github连接:https://github.com/zxing/zxing 以下为修改方法 Step 1: Add following lines to rotate data bef ...
- Problem E: 零起点学算法34——3n+1问题
#include<stdio.h> #include<math.h> int main() { int n; n<=pow(,); ; scanf("%d&qu ...
- (Mark)JS中关于闭包
闭包(Closures) 在ECMAScript中,函数是“第一类”对象.这个名词意味着函数可以作为参数被传递给其他函数使用 (在这种情况下,函数被称为“funargs”——“functional a ...
- iOS framework静态库中使用xib和图片资源详解
一.新建bundle 前2篇文章介绍了iOS 最新framework和.a静态库制作及使用全解 iOS 工程套子工程,主工程和framework工程或.a library静态库工程联调 我现在是在 ...
- MYSQL复习笔记7-索引
Date: 20140207Auth: Jin 索引是根据表中一列或若干列按照一定顺序建立的列值与记录行之间的对应关系表. 索引的主要作用 快速存取数据 保证数据记录的唯一性 实现表与表之间的参照完整 ...
- HDU 4630 No Pain No Game(2013多校3 1010题 离线处理+树状数组求最值)
No Pain No Game Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)T ...