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 ...
随机推荐
- 查看WIFI连接的信号强度
netsh wlan show interface (netsh wlan show interface) -match '^\s+Signal' -replace '\s+Signal\s+:\s+ ...
- Java常用的异常类型
如上图所示:异常Exception和Error都继承自Throwable类 其中Error类代表了编译错误和系统的错误,不允许捕获 Exception代表标准java库方法所触发的异常.包括Runti ...
- Genymotion集成到Eclipse
在Eclipse中使用Genymotion Google的ADT中自带的模拟器速度太慢,可以使用Genymotion代替.关于Genymotion的安装方法,可以直接访问官网,需要注册账号,因为创建模 ...
- SpringMVC转发页面405错误
需要在该方法前加上:@ResponseBody注解 加上这个注解后可能在转发页面的时候出现问题,则需要在方法的参数中增加HttpServletRequest 和HttpServletResponse参 ...
- sharepoint rest 脚本发送邮件
function processSendEmails() { var from = 'asad@Example.com', to = 'someone@Example.com', body = 'He ...
- java String详解
创建字符串: 1. String(char a[]) char a[] = {'g','o','o','d'}; String str = new String(a); 2. String(char ...
- 给大家推荐一款非常好用的表单验证插件:lr-verify.js
废话不说,直接上代码说明,1分钟学会: 例: 1.验证配置 $.extend(Verify.types, { "must" : { "verify" : fun ...
- 自定义loading效果
结合Font Awesome字体图标自定义loading效果 Font Awesome字体图标地址:http://www.fontawesome.com.cn/faicons/ 使用javascrip ...
- redis学习笔记(三)
Spring data redis: 要求: Redis 版本 > 2.6 与 Lettuce 或 Jedis 集成,两种java开源Redis库. Spring redis主要做的两件事: 连 ...
- Hadoop(12)-MapReduce框架原理-Hadoop序列化和源码追踪
1.什么是序列化 2.为什么要序列化 3.为什么不用Java的序列化 4.自定义bean对象实现序列化接口(Writable) 在企业开发中往往常用的基本序列化类型不能满足所有需求,比如在Hadoop ...