STL容器之list源码详细解读
作者:DivineH
这篇文章主要介绍了STL容器之list源码详细解读,相对于vector的连续线性空间,list就显得更加复杂,它每插入或者删除一个元素,就配置或释放一个元素空间,需要的朋友可以参考下
简介
相对于vector的连续线性空间,list就显得更加复杂,它每插入或者删除一个元素,就配置或释放一个元素空间。
因此,list对于空间的利用非常精准,一点也不浪费,而且,对于任何位置的插入或者删除,list永远是常数时间。
构造函数
explicit list(const allocator_type& __a = allocator_type()) : _Base(__a) {} // 构造拥有 n 个有值 value 的元素的容器 list(size_type __n, const _Tp& __value, const allocator_type& __a = allocator_type()) : _Base(__a) { insert(begin(), __n, __value); } explicit list(size_type __n) : _Base(allocator_type()) { insert(begin(), __n, _Tp()); } // 构造拥有范围 [first, last) 内容的容器 list(const _Tp* __first, const _Tp* __last, const allocator_type& __a = allocator_type()) : _Base(__a) { this->insert(begin(), __first, __last); } list(const_iterator __first, const_iterator __last, const allocator_type& __a = allocator_type()) : _Base(__a) { this->insert(begin(), __first, __last); } // 拷贝构造函数 list(const list<_Tp, _Alloc>& __x) : _Base(__x.get_allocator()) { insert(begin(), __x.begin(), __x.end()); }
对于list中的每一个节点,都被封装成了_List_node对象:
// 双向链表 struct _List_node_base { _List_node_base* _M_next; _List_node_base* _M_prev; }; // list 节点 template <class _Tp> struct _List_node : public _List_node_base { _Tp _M_data; // 节点存储的值 };
即list是通过双向循环链表来组织节点的。
主要函数
push_back
void push_back(const _Tp& __x) { insert(end(), __x); } // 插入一个节点,作为尾节点 // 在__position之前插入节点__x iterator insert(iterator __position, const _Tp& __x) { _Node* __tmp = _M_create_node(__x); // 调整双向指针,插入新元素 __tmp->_M_next = __position._M_node; // list为双向链表 __tmp->_M_prev = __position._M_node->_M_prev; __position._M_node->_M_prev->_M_next = __tmp; __position._M_node->_M_prev = __tmp; return __tmp; }
push_back插入一个节点,作为尾结点,都是双向链表的常用操作,这里不再赘述。
push_front
void push_front(const _Tp& __x) { insert(begin(), __x); } // 插入一个节点 __x,作为头结点 iterator insert(iterator __position, const _Tp& __x) { _Node* __tmp = _M_create_node(__x); // 调整双向指针,插入新元素 __tmp->_M_next = __position._M_node; // list为双向链表 __tmp->_M_prev = __position._M_node->_M_prev; __position._M_node->_M_prev->_M_next = __tmp; __position._M_node->_M_prev = __tmp; return __tmp; }
push_front同样是调用了insert(iterator, const _Tp&)来完成插入操作的。
clear
void clear() { _Base::clear(); } template <class _Tp, class _Alloc> void _List_base<_Tp,_Alloc>::clear() { _List_node<_Tp>* __cur = (_List_node<_Tp>*) _M_node->_M_next; // 指向开始节点,begin() while (__cur != _M_node) { _List_node<_Tp>* __tmp = __cur; __cur = (_List_node<_Tp>*) __cur->_M_next; _Destroy(&__tmp->_M_data); // 销毁(析构并释放)一个节点 _M_put_node(__tmp); } // 恢复 _M_node 原始状态 _M_node->_M_next = _M_node; _M_node->_M_prev = _M_node; }
clear时从头结点到尾结点,依次释放每一个节点的内存空间。
特点
由于list是通过双向链表来实现的,它的迭代器要提供前移、后移的能力,所以list提供了Bidirectional iterator; 插入、删除节点的效率很高。
与vector的区别
- vector为存储的对象分配一块连续的地址空间,随机访问效率很高。但是插入和删除需要移动大量的数据,效率较低。尤其当vector中存储的对象较大,或者构造函数复杂,则在对现有的元素进行拷贝的时候会执行拷贝构造函数。
- list中的对象是离散的,随机访问需要遍历整个链表,访问效率比vector低。但是在list中插入元素,尤其在首尾插入,效率很高,只需要改变元素的指针。
- vector是单向的,而list是双向的;
- 向量中的iterator在使用后就释放了,但是链表list不同,它的迭代器在使用后还可以继续用;链表特有的;
到此这篇关于STL容器之list源码详细解读的文章就介绍到这了,更多相关STL容器list 内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!