stl源码分析之list
本文主要分析gcc4.8版本的stl list的源码实现,与vector的线性空间结构不同,list的节点是任意分散的,节点之间通过指针连接,好处是在任何位置插入删除元素都只需要常数时间,缺点是不能随机访问,查询复杂度是O(n),n为list中的元素个数。所以list非常适合应用与数据插入删除频繁的场景。
一、 list节点
list节点定义如下,
struct _List_node_base
{
_List_node_base* _M_next;
_List_node_base* _M_prev;
}
template<typename _Tp>
struct _List_node : public __detail::_List_node_base
{
_Tp _M_data;
}
很明显list是一个双向链表,_M_next和_M_prev分别指向下一个和上一个节点,_M_data是节点存储的数据。
二、 list的迭代器
由于list不是线性空间结构,通用迭代器无法正常移动,所以list需要定义专门的迭代器。
template<typename _Tp>
struct _List_iterator
{
typedef _List_iterator<_Tp> _Self;
typedef _List_node<_Tp> _Node; typedef ptrdiff_t difference_type;
typedef std::bidirectional_iterator_tag iterator_category;
typedef _Tp value_type;
typedef _Tp* pointer;
typedef _Tp& reference; _List_iterator()
: _M_node() { } explicit
_List_iterator(__detail::_List_node_base* __x)
: _M_node(__x) { } // Must downcast from _List_node_base to _List_node to get to _M_data.
reference
operator*() const
{ return static_cast<_Node*>(_M_node)->_M_data; } pointer
operator->() const
{ return std::__addressof(static_cast<_Node*>(_M_node)->_M_data); } _Self&
operator++()
{
_M_node = _M_node->_M_next;
return *this;
} _Self
operator++(int)
{
_Self __tmp = *this;
_M_node = _M_node->_M_next;
return __tmp;
} _Self&
operator--()
{
_M_node = _M_node->_M_prev;
return *this;
} _Self
operator--(int)
{
_Self __tmp = *this;
_M_node = _M_node->_M_prev;
return __tmp;
}
// 指向list节点指针
__detail::_List_node_base* _M_node;
};
list迭代器指向list的node,所以解引用时必须返回 static_cast<_Node*>(_M_node)->_M_data。list迭代器不是随机访问迭代器,必须通过节点的next和prev指针一步步移动,元素访问复杂度是线性的。另外还定义了只读迭代器_List_const_iterator。
三、 list的定义
和vector一样,list继承于_List_base,内存分配释放工作由_List_base负责,
template<typename _Tp, typename _Alloc>
class _List_base
{
protected:
//使用traits方法,得到_List_node<_Tp>的内存分配器
typedef typename _Alloc::template rebind<_List_node<_Tp> >::other
_Node_alloc_type;
typedef typename _Alloc::template rebind<_Tp>::other _Tp_alloc_type; struct _List_impl
: public _Node_alloc_type
{
//数据成员就只有一个头节点
__detail::_List_node_base _M_node;
_List_impl()
: _Node_alloc_type(), _M_node()
{ }
_List_impl(const _Node_alloc_type& __a)
: _Node_alloc_type(__a), _M_node() { } };
_List_impl _M_impl;
//分配节点的接口
_List_node<_Tp>* _M_get_node()
{ return _M_impl._Node_alloc_type::allocate(); }
_List_base()
: _M_impl()
{ _M_init(); }
//list初始为空,头节点的next和prev都指向自身
void _M_init()
{
this->_M_impl._M_node._M_next = &this->_M_impl._M_node;
this->_M_impl._M_node._M_prev = &this->_M_impl._M_node;
}
//从第一个数据节点开始依次删除并释放内存。
template<typename _Tp, typename _Alloc>
void _List_base<_Tp, _Alloc>:: _M_clear()
{
typedef _List_node<_Tp> _Node;
_Node* __cur = static_cast<_Node*>(_M_impl._M_node._M_next);
while (__cur != &_M_impl._M_node)
{
_Node* __tmp = __cur;
__cur = static_cast<_Node*>(__cur->_M_next);
_M_get_Node_allocator().destroy(__tmp);
_M_put_node(__tmp);
}
}
list继承了_List_base的头节点,
template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
class list : protected _List_base<_Tp, _Alloc>
{
// concept requirements
typedef typename _Alloc::value_type _Alloc_value_type;
__glibcxx_class_requires(_Tp, _SGIAssignableConcept)
__glibcxx_class_requires2(_Tp, _Alloc_value_type, _SameTypeConcept) typedef _List_base<_Tp, _Alloc> _Base;
typedef typename _Base::_Tp_alloc_type _Tp_alloc_type;
typedef typename _Base::_Node_alloc_type _Node_alloc_type; public:
typedef _Tp value_type;
typedef typename _Tp_alloc_type::pointer pointer;
typedef typename _Tp_alloc_type::const_pointer const_pointer;
typedef typename _Tp_alloc_type::reference reference;
typedef typename _Tp_alloc_type::const_reference const_reference;
typedef _List_iterator<_Tp> iterator;
typedef _List_const_iterator<_Tp> const_iterator;
typedef std::reverse_iterator<const_iterator> const_reverse_iterator;
typedef std::reverse_iterator<iterator> reverse_iterator;
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef _Alloc allocator_type;
......
四、 list构造方式
list提供了多种构造函数,
//默认构造函数
list()
: _Base() { }
//初始化为n个默认值
explicit
list(size_type __n)
: _Base()
{ _M_default_initialize(__n); }
//初始化为n个__value值
list(size_type __n, const value_type& __value,
const allocator_type& __a = allocator_type())
: _Base(_Node_alloc_type(__a))
{ _M_fill_initialize(__n, __value); }
//拷贝构造函数
list(const list& __x)
: _Base(__x._M_get_Node_allocator())
{ _M_initialize_dispatch(__x.begin(), __x.end(), __false_type()); }
//移动构造函数
list(list&& __x) noexcept
: _Base(std::move(__x)) { }
五、 list元素操作
篇幅有限,只讨论部分函数。
iterator begin() _GLIBCXX_NOEXCEPT
{ return iterator(this->_M_impl._M_node._M_next); }
iterator end() _GLIBCXX_NOEXCEPT
{ return iterator(&this->_M_impl._M_node); }
end指向头节点,begin指向第一个数据节点,begin和end仍然组成一个前闭后开区间。
Bool empty() const _GLIBCXX_NOEXCEPT
{ return this->_M_impl._M_node._M_next == &this->_M_impl._M_node; }
通过查看头节点是否指向自身来判断list是否为空。
void push_front(const value_type& __x)
{ this->_M_insert(begin(), __x); }
void push_back(const value_type& __x)
{ this->_M_insert(end(), __x); }
template<typename... _Args> void _M_insert(iterator __position, _Args&&... __args)
{
_Node* __tmp = _M_create_node(std::forward<_Args>(__args)...);
__tmp->_M_hook(__position._M_node);
}
template<typename _Tp, typename _Alloc>
typename list<_Tp, _Alloc>::iterator
list<_Tp, _Alloc>::
insert(iterator __position, const value_type& __x)
{
_Node* __tmp = _M_create_node(__x);
__tmp->_M_hook(__position._M_node);
return iterator(__tmp);
}
push_front,push_back和insert的时间复杂度都是O(1).
template<typename _Tp, typename _Alloc>
void list<_Tp, _Alloc>::merge(list&& __x)
{
if (this != &__x)
{
_M_check_equal_allocators(__x); iterator __first1 = begin();
iterator __last1 = end();
iterator __first2 = __x.begin();
iterator __last2 = __x.end();
while (__first1 != __last1 && __first2 != __last2)
if (*__first2 < *__first1)
{
iterator __next = __first2;
_M_transfer(__first1, __first2, ++__next);
__first2 = __next;
}
else
++__first1;
if (__first2 != __last2)
_M_transfer(__last1, __first2, __last2);
}
}
stl源码分析之list的更多相关文章
- STL源码分析《4》----Traits技术
在 STL 源码中,到处可见 Traits 的身影,其实 Traits 不是一种语法,更确切地说是一种技术. STL库中,有一个函数叫做 advance, 用来将某个迭代器(具有指针行为的一种 cla ...
- STL源码分析《3》----辅助空间不足时,如何进行归并排序
两个连在一起的序列 [first, middle) 和 [middle, last) 都已经排序, 归并排序最核心的算法就是 将 [first, middle) 和 [middle, last) 在 ...
- STL 源码分析《1》---- list 归并排序的 迭代版本, 神奇的 STL list sort
最近在看 侯捷的 STL源码分析,发现了以下的这个list 排序算法,乍眼看去,实在难以看出它是归并排序. 平常大家写归并排序,通常写的是 递归版本..为了效率的考虑,STL库 给出了如下的 归并排序 ...
- STL源码分析读书笔记--第二章--空间配置器(allocator)
声明:侯捷先生的STL源码剖析第二章个人感觉讲得蛮乱的,而且跟第三章有关,建议看完第三章再看第二章,网上有人上传了一篇读书笔记,觉得这个读书笔记的内容和编排还不错,我的这篇总结基本就延续了该读书笔记的 ...
- STL 源码分析《2》----nth_element() 使用与源码分析
Select 问题: 在一个无序的数组中 找到第 n 大的元素. 思路 1: 排序,O(NlgN) 思路 2: 利用快排的 RandomizedPartition(), 平均复杂度是 O(N) 思路 ...
- stl源码分析之allocator
allocator封装了stl标准程序库的内存管理系统,标准库的string,容器,算法和部分iostream都是通过allocator分配和释放内存的.标准库的组件有一个参数指定使用的allocat ...
- STL源码分析与实现-stl_list容器
1. stl_list 介绍 今天我们来总结一下stl_List, 通过之前介绍单链表的文章,其实对链表的基本操作已经十分熟悉了,那对于stl_list,无非就是链表结构不一样,至于其中的增删改查的细 ...
- STL 源码分析六大组件-allocator
1. allocator 基本介绍 分配器(allocator))是C ++标准库的一个组件, 主要用来处理所有给定容器(vector,list,map等)内存的分配和释放.C ++标准库提供了默认使 ...
- STL源码分析之迭代器
前言 迭代器是将算法和容器两个独立的泛型进行调和的一个接口. 使我们不需要关系中间的转化是怎么样的就都能直接使用迭代器进行数据访问. 而迭代器最重要的就是对operator *和operator-&g ...
- STL 源码分析《5》---- lower_bound and upper_bound 详解
在 STL 库中,关于二分搜索实现了4个函数. bool binary_search (ForwardIterator beg, ForwardIterator end, const T& v ...
随机推荐
- selenium + python自动化测试unittest框架学习(三)webdriver元素操作(二)
上一篇是元素的定位,那么定位元素的目的就是对元素进行操作,例如写入文本,点击按钮,拖动等等的操作 (1)简单元素操作 简单元素操作 find_element_by_id("kw") ...
- Css3 实现丝带效果
代码如下: <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF ...
- [转]浅谈利用SQLite存储离散瓦片的思路和实现方法
文章版权由作者李晓晖和博客园共有,若转载请于明显处标明出处:http://www.cnblogs.com/naaoveGIS/ 1.背景 在多个项目中涉及到互联网地图的内网显示,通过自制工具完成了互联 ...
- 客户端对象模型之Excel数据导入到列表
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <t ...
- pt-archiver数据归档
可以使用percona-toolkit包中的pt-archiver工具来进行历史数据归档 pt-archiver使用的场景: 1.清理线上过期数据. 2.清理过期数据,并把数据归档到本地归档表中,或者 ...
- python通过xlsxwriter模块将文字写入xlsx文件
#今天和大家一起学习通过python的xlsxwriter模块 xlsxwriter模块主要用来生成excel表格,插入数据.插入图标等表格操作等. 环境:python 3 1)安装 xlsxwrit ...
- 服务端接收不到ajax post请求的参数
问题描述 服务端使用request.getParameter()接收不到post请求的参数,导致业务逻辑抛出空指针异常. 解决途径 tomcat对post请求支持的字节数不受限制的配置发生变化.在to ...
- 初次使用vue-cli3 来搭建项目
1,细数项目中使用的技术:vue, vue-router, vuex ,axios,vue-cli3, 快速建站. 2,mock技术使用的express-mockjs . 由于cli3 最新版的话缺少 ...
- python面试题之基础
一.基础语法 1. 输入与输出 1.1 代码中要修改不可变数据会出现什么问题? 抛出什么异常? (2018-3-29-lxy) 代码不会正常运行,抛出 TypeError 异常. 1.2a=1,b=2 ...
- 面试:HashSet怎么判断重复
HashSet是基于HashMap实现的,元素的值存储在key上,value的值所有元素都一样,都是这个 private static final Object PRESENT = new Objec ...