vector是初次了解STL接触最多的一个容器,是一种很方便的数组替代品,不需要显示指定容量大小,其内部可以根据需要进行自动扩容操作。也正因为这个特性,每次扩容的时候都会伴随着“配置新空间 / 移动旧数据 / 释放旧空间”的操作,因此是有一定的时间成本的,当然vector提供了reserve接口,如果能够对元素个数有一个大概的了解,那么可以一开始就分配合适的空间。vector的内存空间是连续的,因此对插入元素的操作而言,在vector尾部插入才是合适的选择。
1. _Vector_base 基类
SGI STL的vector拥有一个叫做_Vector_base基类,该类主要定义一个基本框架(start、finish、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 | // vector 基类 _Vector_base 定义 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) {} // 构造函数:指定节点个数n _Vector_base(size_t __n, const _Alloc&) : _M_start(0), _M_finish(0), _M_end_of_storage(0) { // 分配 n 个节点所需空间 _M_start = _M_allocate(__n); // _M_start 指向起始位置 _M_finish = _M_start; // _M_finish 指向其实位置 _M_end_of_storage = _M_start + __n; // _M_end_of_storage指向内存末尾节点 } // 析构函数:释放内存空间 ~_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; // 分配 n 个节点所需要的空间 _Tp* _M_allocate(size_t __n) { return _M_data_allocator::allocate(__n); } // 释放 n 个节点所对应的空间 void _M_deallocate(_Tp* __p, size_t __n) { _M_data_allocator::deallocate(__p, __n); } }; |
2. vector 成员函数分析
迭代器相关函数定义,包括begin(), end() 以及 const版本、reverse版本、reverse const版本:
iterator begin() { return _M_start; } const_iterator begin() const { return _M_start; } iterator end() { return _M_finish; } const_iterator end() const { return _M_finish; } reverse_iterator rbegin() { return reverse_iterator(end()); } const_reverse_iterator rbegin() const { return const_reverse_iterator(end()); } reverse_iterator rend() { return reverse_iterator(begin()); } const_reverse_iterator rend() const { return const_reverse_iterator(begin()); } |
元素个数相关定义:
// 元素个数 size_type size() const { return size_type(end() - begin()); } // 最大元素个数:超出这个数目将会溢出32位空间 size_type max_size() const { return size_type(-1) / sizeof(_Tp); } // 容量:现有空间能够容纳的元素个数 size_type capacity() const { return size_type(_M_end_of_storage - begin()); } // 是否为空 bool empty() const { return begin() == end(); } |
下标访问操作,包括不检查是否越界的[]操作符,以及检查是否越界的at函数:
// []操作符:non-const / const reference operator[](size_type __n) { return *(begin() + __n); } const_reference operator[](size_type __n) const { return *(begin() + __n); } // 如果元素索引越界则抛出异常 void _M_range_check(size_type __n) const { if (__n >= this->size()) __stl_throw_range_error("vector"); } // 通过at访问元素会检查是否越界 reference at(size_type __n) { _M_range_check(__n); return (*this)[__n]; } const_reference at(size_type __n) const { _M_range_check(__n); return (*this)[__n]; } |
构造函数与析构函数:
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 38 39 40 41 42 43 44 45 46 47 | // 无参数的构造函数 explicit vector(const allocator_type& __a = allocator_type()) : _Base(__a) {} // 指定元素个数n以及元素初始值的构造函数 vector(size_type __n, const _Tp& __value, const allocator_type& __a = allocator_type()) : _Base(__n, __a) { _M_finish = uninitialized_fill_n(_M_start, __n, __value); } // 指定元素个数n的构造函数 explicit vector(size_type __n) : _Base(__n, allocator_type()) { _M_finish = uninitialized_fill_n(_M_start, __n, _Tp()); } // 复制构造函数 vector(const vector<_Tp, _Alloc>& __x) : _Base(__x.size(), __x.get_allocator()) { _M_finish = uninitialized_copy(__x.begin(), __x.end(), _M_start); } #ifdef __STL_MEMBER_TEMPLATES // 迭代器传参构造函数 template <class _InputIterator> vector(_InputIterator __first, _InputIterator __last, const allocator_type& __a = allocator_type()) : _Base(__a) { typedef typename _Is_integer<_InputIterator>::_Integral _Integral; _M_initialize_aux(__first, __last, _Integral()); } template <class _Integer> void _M_initialize_aux(_Integer __n, _Integer __value, __true_type) { _M_start = _M_allocate(__n); _M_end_of_storage = _M_start + __n; _M_finish = uninitialized_fill_n(_M_start, __n, __value); } template <class _InputIterator> void _M_initialize_aux(_InputIterator __first, _InputIterator __last, __false_type) { _M_range_initialize(__first, __last, __ITERATOR_CATEGORY(__first)); } #else vector(const _Tp* __first, const _Tp* __last, const allocator_type& __a = allocator_type()) : _Base(__last - __first, __a) { _M_finish = uninitialized_copy(__first, __last, _M_start); } #endif /* __STL_MEMBER_TEMPLATES */ // 析构函数:destroy负责对元素进行析构操作,空间回收由父类负责 ~vector() { destroy(_M_start, _M_finish); } |
需要注意的是第5行所在的构造函数和第20行所在的构造函数可能产生冲突,因为对第20行的构造函数而言,如果first和last都是数值的话,存在两种情况:
1. first和last是指针,迭代器也是指针,也就是first和last确实是迭代器;
2. first是n,last是value,也就是first和last是整形;
这里通过上一篇文章《STL笔记之迭代器》中提到的type_traits技术来进行鉴别,然后通过重载机制选择正确的实现版本。
reserve函数:If n is less than or equal to capacity(), this call has no effect. 只有设定的大小n比旧的容量大时才有作用。
void reserve(size_type __n) { // 如果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; } } |
swap成员函数:实际上是交换了两个vector内部的三个指针,因此效率是非常高的!
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进行了重载操作:
template <class _Tp, class _Alloc> inline void swap(vector<_Tp, _Alloc>& __x, vector<_Tp, _Alloc>& __y) { __x.swap(__y); } |
插入以及删除操作:
// 在指定位置插入一个元素 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; } // 删除末尾元素 void pop_back() { --_M_finish; destroy(_M_finish); } // 删除指定位置的元素 iterator erase(iterator __position) { // 如果不是最后一个元素,那么把后面的元素往前移动 if (__position + 1 != end()) copy(__position + 1, _M_finish, __position); // 删除最后一个元素 --_M_finish; destroy(_M_finish); return __position; } // 删除指定区间内的元素 iterator erase(iterator __first, iterator __last) { // 后面的元素往前移动 iterator __i = copy(__last, _M_finish, __first); // 析构末尾的元素 destroy(__i, _M_finish); // 调整指针 _M_finish = _M_finish - (__last - __first); return __first; } // resize操作 void resize(size_type __new_size, const _Tp& __x) { // 尺寸变小:删除末尾多余的元素 if (__new_size < size()) erase(begin() + __new_size, end()); // 尺寸变大:往末尾插入新元素 else insert(end(), __new_size - size(), __x); } void resize(size_type __new_size) { resize(__new_size, _Tp()); } // 清空所有元素 void clear() { erase(begin(), end()); } |
赋值操作符:
template <class _Tp, class _Alloc> vector<_Tp,_Alloc>& vector<_Tp,_Alloc>::operator=(const vector<_Tp, _Alloc>& __x) { // 如果是自己赋值给自己那么不进行任何操作 if (&__x != this) { const size_type __xlen = __x.size(); // 如果 new size > cur capacity,则必须分配新的空间 if (__xlen > capacity()) { iterator __tmp = _M_allocate_and_copy(__xlen, __x.begin(), __x.end()); destroy(_M_start, _M_finish); _M_deallocate(_M_start, _M_end_of_storage - _M_start); _M_start = __tmp; _M_end_of_storage = _M_start + __xlen; } // 如果new size <= cur size,则复制新的元素,并析构末尾的旧元素 else if (size() >= __xlen) { iterator __i = copy(__x.begin(), __x.end(), begin()); destroy(__i, _M_finish); } // 如果new size > cur size 并且容量足够,则直接复制即可 else { copy(__x.begin(), __x.begin() + size(), _M_start); uninitialized_copy(__x.begin() + size(), __x.end(), _M_finish); } // 调整指针 _M_finish = _M_start + __xlen; } return *this; } |
3. vector 动态扩容机制
vector的push_back源码如下,当还有可用空间的时候,直接在末尾构造一个新的节点,否则通过调用辅助函数_M_insert_aux来实现。
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_insert_aux中,仍然会判断空间是否足够,不够的情况下会进行“配置新空间 / 移动旧数据 / 释放旧空间”的操作,空间是成倍增长的,_M_insert_aux的源代码如下:
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)); // 调整finish指针 ++_M_finish; // 临时对象 _Tp __x_copy = __x; // copy_backward(first, last, result); // 复制[first, last) 到 [result - (last - first), result) // 即[position, finfish-2)往后移动一个节点 copy_backward(__position, _M_finish - 2, _M_finish - 1); // 插入元素 *__position = __x_copy; } // 空间不够用了,需要重新配置 else { // 旧的大小 const size_type __old_size = size(); // 如果旧的大小为0,那么新的大小为1 // 如果旧的大小不为0,那么新的大小为旧的两倍 const size_type __len = __old_size != 0 ? 2 * __old_size : 1; // 分配新的内存区块 iterator __new_start = _M_allocate(__len); iterator __new_finish = __new_start; __STL_TRY { // 复制旧的区间内容 [start, position) __new_finish = uninitialized_copy(_M_start, __position, __new_start); // 插入元素到新的区间的position位置 construct(__new_finish, __x); // new finish后移 ++__new_finish; // 复制position之后的元素 [position, 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; } } |
vector空间成倍增长示意图如下:
4. 插入n个元素
如果在某一个位置插入n个元素,那么空间的增长会有一点点不同。
如果空间不够,那么两倍于旧空间大小的尺寸可能也是不够的,因此要做一个判断:
new_size = old_size + max(old_size, n);
template <class _Tp, class _Alloc> void vector<_Tp, _Alloc>::_M_fill_insert(iterator __position, size_type __n, const _Tp& __x) { // 如果n为0那么什么都不做 if (__n != 0) { // 如果剩余空间足够插入n个元素 if (size_type(_M_end_of_storage - _M_finish) >= __n) { _Tp __x_copy = __x; const size_type __elems_after = _M_finish - __position; iterator __old_finish = _M_finish; if (__elems_after > __n) { uninitialized_copy(_M_finish - __n, _M_finish, _M_finish); _M_finish += __n; copy_backward(__position, __old_finish - __n, __old_finish); fill(__position, __position + __n, __x_copy); } else { uninitialized_fill_n(_M_finish, __n - __elems_after, __x_copy); _M_finish += __n - __elems_after; uninitialized_copy(__position, __old_finish, _M_finish); _M_finish += __elems_after; fill(__position, __old_finish, __x_copy); } } // 空间不够用了,需要重新分配空间 else { const size_type __old_size = size(); // 新的尺寸 >= 2倍于旧的尺寸 const size_type __len = __old_size + max(__old_size, __n); // 分配空间并调整指针 iterator __new_start = _M_allocate(__len); iterator __new_finish = __new_start; __STL_TRY { // 复制旧区间[start,position)元素 __new_finish = uninitialized_copy(_M_start, __position, __new_start); // 插入n个x元素的副本 __new_finish = uninitialized_fill_n(__new_finish, __n, __x); // 复制旧区间[position, finish)元素 __new_finish = uninitialized_copy(__position, _M_finish, __new_finish); } // 如果发生异常则进行回滚操作 __STL_UNWIND((destroy(__new_start,__new_finish), _M_deallocate(__new_start,__len))); // 析构旧空间里的元素 destroy(_M_start, _M_finish); // 回收旧空间 _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; } } } |
STL源码剖析笔记系列
1. STL笔记之空间配置器
2. STL笔记之迭代器
3. STL笔记之vector
本文地址: 程序人生 >> STL笔记之vector
作者:代码疯子(Wins0n) 本站内容如无声明均属原创,转载请保留作者信息与原文链接,谢谢!