vector可以说用到最多的容器了。今天来从源码分析它。
1. _Vector_base
·
先看类图:
_Vector_base
是 vector
的模板基类,模板接收一个值类型_Tp
, 和一个分配器类型_Alloc
, 成员变量有三个 _M_start
, _M_finish
, _M_end_of_storage
。 示意图如下:
_M_start
:当前可插入的第一个位置
_M_finish
: 当前可插入的最后一个位置的后一个位置
_M_end_of_storage
: 当前申请的内存单元个数
代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
|
template <class _Tp, class _Alloc> class _Vector_base { public: typedef _Alloc allocator_type; allocator_type get_allocator() const { return allocator_type(); } _Vector_base(const _Alloc&) : _M_start(0), _M_finish(0), _M_end_of_storage(0) {} _Vector_base(size_t __n, const _Alloc&) : _M_start(0), _M_finish(0), _M_end_of_storage(0) { _M_start = _M_allocate(__n); _M_finish = _M_start; _M_end_of_storage = _M_start + __n; } ~_Vector_base() { _M_deallocate(_M_start, _M_end_of_storage - _M_start); }
protected: _Tp* _M_start; _Tp* _M_finish; _Tp* _M_end_of_storage;
typedef simple_alloc<_Tp, _Alloc> _M_data_allocator; _Tp* _M_allocate(size_t __n) { return _M_data_allocator::allocate(__n); } void _M_deallocate(_Tp* __p, size_t __n) { _M_data_allocator::deallocate(__p, __n); } };
|
可以看到_Vector_base
无特别的用途,仅是包装了三个重要变量和分配器类型(还记得vector的默认分配类型是什么吗?)
2. vector·
通常来说,使用vector的接口包括:
- reserve: 预留capacity
- push_back: 尾追加
- pop_back: 尾删除
- insert: 插入
- swap: 交换(通常用于快速析构释放内存)
- Iterator
逐个说下如上接口是如何实现的:
1. reserve·
reserve
可能涉及内存分配与拷贝。推荐在构造vector
后,立即调用reserve
,而不要再“使用”过vector后,再reserve,除非明确要“扩容”到某个大小。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| size_type capacity() const { return size_type(_M_end_of_storage - begin()); }
void reserve(size_type __n) { if (capacity() < __n) { const size_type __old_size = size(); iterator __tmp = _M_allocate_and_copy(__n, _M_start, _M_finish); destroy(_M_start, _M_finish); _M_deallocate(_M_start, _M_end_of_storage - _M_start); _M_start = __tmp; _M_finish = __tmp + __old_size; _M_end_of_storage = _M_start + __n; } }
|
- 如果
capacity
比传入的 __n
大,不做任何事情
- 否则:分配
__n
个元素大小的内存,将原先的 _M_start
到 _M_finish
之间的内存拷贝到这片内存上,并返回拷贝后的末尾地址返给__tmp
- 析构
_M_start
到_M_finish
之间的对象
- 释放
_M_start
到_M_finish
之间的内存
- 更新
_M_start
,_M_finish
和_M_end_of_storage
指针
2. push_back·
push_back可能扩容,扩容的机制是2倍扩容。
1 2 3 4 5 6 7 8 9
| void push_back(const _Tp& __x) { if (_M_finish != _M_end_of_storage) { construct(_M_finish, __x); ++_M_finish; } else _M_insert_aux(end(), __x); }
|
如果有备用空间,则直接在_M_finish
上构造对象,并++_M_finish
否则,调用_M_insert_aux
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| template <class _Tp, class _Alloc> void vector<_Tp, _Alloc>::_M_insert_aux(iterator __position, const _Tp& __x) { if (_M_finish != _M_end_of_storage) { construct(_M_finish, *(_M_finish - 1)); ++_M_finish; _Tp __x_copy = __x; copy_backward(__position, _M_finish - 2, _M_finish - 1); *__position = __x_copy; } else { const size_type __old_size = size(); const size_type __len = __old_size != 0 ? 2 * __old_size : 1; iterator __new_start = _M_allocate(__len); iterator __new_finish = __new_start; __STL_TRY { __new_finish = uninitialized_copy(_M_start, __position, __new_start); construct(__new_finish, __x); ++__new_finish; __new_finish = uninitialized_copy(__position, _M_finish, __new_finish); } __STL_UNWIND((destroy(__new_start,__new_finish), _M_deallocate(__new_start,__len))); destroy(begin(), end()); _M_deallocate(_M_start, _M_end_of_storage - _M_start); _M_start = __new_start; _M_finish = __new_finish; _M_end_of_storage = __new_start + __len; } }
|
看else
分支即可:
- 如果当前size为0, 则分配1个对象内存大小,否则扩容至两倍大小。(也就是说vector的内存分配是 1, 2, 4, 8… 的策略)
- 先将
_M_start
到 __position
位置处的所有内存拷贝至 __new_finish
处
- 在
__new_finish
处构造 __x
,并++_new_finish
- 将
__position
到_M_finish
处的所有内存,拷贝至__new_finish
- 最后析构并释放原来的老内存
- 更新三个重要的成员变量
3. pop_back·
1 2 3 4 5
| void pop_back() { --_M_finish; destroy(_M_finish); }
|
析构但并不释放内存。
思考: 看样子老版本的vector有个缺点:如果在过去的整个时间段内,只有非常短的时间里将vector插得很长,但只要vector不完全析构,这片很长的内存得不到释放。 老版本只能通过 swap
的机制来完全释放内存,不像新版本可以使用shrink_to_fit
可以灵活释放多余内存
4. insert·
看过 push_back
后, insert很简单:
1 2 3 4 5 6 7 8 9 10 11
| iterator insert(iterator __position, const _Tp& __x) { size_type __n = __position - begin(); if (_M_finish != _M_end_of_storage && __position == end()) { construct(_M_finish, __x); ++_M_finish; } else _M_insert_aux(__position, __x); return begin() + __n; }
|
若插入点为end()
, 则直接插入。 否则走 _M_insert_aux
流程。
5. swap·
1 2 3 4 5 6 7
| void swap(vector<_Tp, _Alloc>& __x) { __STD::swap(_M_start, __x._M_start); __STD::swap(_M_finish, __x._M_finish); __STD::swap(_M_end_of_storage, __x._M_end_of_storage); }
|
swap非常简单,和x
对象交换内部三个重要的成员变量,此外,注意交换的对象的模板要完全一致,包括分配器的类型。这个操作的cost非常低,一般用于释放原内存,以及多并发的时候,将全局的某个带锁的vector的内存吐到局部不带锁的vector, 减少锁竞争。
如:
1 2 3 4 5 6 7
| std::vector<int> vec1;
{ std::vector<int> vec2; vec1.swap(vec2); }
|
6. iterator相关·
vector的iterator实际上就是value指针:
1 2
| typedef _Tp value_type; typedef value_type* iterator;
|
所以:
1 2 3 4
| iterator begin() { return _M_start; } const_iterator begin() const { return _M_start; } iterator end() { return _M_finish; } const_iterator end() const { return _M_finish; }
|
如上iterator函数族直接返回的是内存的成员变量。
3. 总结·
vector容器相对简单,注意点主要是 容量不足时的扩容策略(2倍扩容),另外,vector也不是线程安全的,使用时需要拷贝数据竞争问题。