C++ STL 容器设计与实现分析 -- vector
2、vector
vector 属于动态数组,自动控制存储空间,根据需要进行扩展和收缩。通常拥有额外的空间以处理未来的增长,这样,每次插入元素时,不需要每次重新分配内存,仅在附加内存耗尽时才重新分配。
可使用 capacity() 查看分配的内存总量,也可以调用 shrink_to_fit() 将额外的内存返回给系统。如果事先知道元素数量,可调用 reserve(n) 一次性分配内存。
vector 继承于 _Vector_base,_Vector_base 只有一个 _Vector_impl 类型的成员变量。
template<typename _Tp, typename _Alloc> struct _Vector_base { struct _Vector_impl_data { pointer _M_start; pointer _M_finish; pointer _M_end_of_storage; /* ... */ }; struct _Vector_impl : public _Tp_alloc_type, public _Vector_impl_data { /* ... */ }; public: _Vector_impl _M_impl; /* ... */ }; template<typename _Tp, typename _Alloc = std::allocator<_Tp> > class vector : protected _Vector_base<_Tp, _Alloc> { /* ... */ };
抽丝剥茧,vector 实现只用到三个指针:
- _M_start:内存起始地址
- _M_finish:vector 结束地址
- _M_end_of_storage:内存结束地址
2.1、 _Vector_base
_Vector_base 负责管理 vector 内存的申请与释放。
_M_allocate() 和 _M_deallocate() 两个函数分别负责申请和释放内存,_M_create_storage(n) 申请 n 个对象的空间,然后初始化三个指针成员。
_GLIBCXX20_CONSTEXPR pointer _M_allocate(size_t __n) { typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr; return __n != 0 ? _Tr::allocate(_M_impl, __n) : pointer(); } _GLIBCXX20_CONSTEXPR void _M_deallocate(pointer __p, size_t __n) { typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr; if (__p) _Tr::deallocate(_M_impl, __p, __n); } protected: _GLIBCXX20_CONSTEXPR void _M_create_storage(size_t __n) { this->_M_impl._M_start = this->_M_allocate(__n); this->_M_impl._M_finish = this->_M_impl._M_start; this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n; }
_Vector_base 构造函数调用 _M_create_storage() 函数申请 n 个对象空间。
_GLIBCXX20_CONSTEXPR _Vector_base(size_t __n, const allocator_type& __a) : _M_impl(__a) { _M_create_storage(__n); }
2.2、vector::type
template<typename _Tp, typename _Alloc = std::allocator<_Tp> > class vector : protected _Vector_base<_Tp, _Alloc> { typedef _Vector_base<_Tp, _Alloc> _Base; typedef typename _Base::_Tp_alloc_type _Tp_alloc_type; typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Alloc_traits; public: typedef _Tp value_type; typedef typename _Base::pointer pointer; typedef typename _Alloc_traits::const_pointer const_pointer; typedef typename _Alloc_traits::reference reference; typedef typename _Alloc_traits::const_reference const_reference; typedef __gnu_cxx::__normal_iterator<pointer, vector> iterator; typedef __gnu_cxx::__normal_iterator<const_pointer, vector> 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; /* ... */ };
2.3、vector::ctor
构造函数分析三种
2.3.1、vector(n)
当构造函数指定了 vector 的大小,首先基类 _Vector_base 申请 n 个元素的内存空间(不调用元素构造函数),然后 vector 构造函数将调用 _M_default_initialize() 函数给申请的 n 个对象初始化。
vector(size_type __n, const allocator_type& __a = allocator_type()) : _Base(_S_check_init_len(__n, __a), __a) { _M_default_initialize(__n); }
_M_default_initialize()
_GLIBCXX20_CONSTEXPR void _M_default_initialize(size_type __n) { this->_M_impl._M_finish = std::__uninitialized_default_n_a(this->_M_impl._M_start, __n, _M_get_Tp_allocator()); } template<typename _ForwardIterator, typename _Size, typename _Tp> _GLIBCXX20_CONSTEXPR inline _ForwardIterator __uninitialized_default_n_a(_ForwardIterator __first, _Size __n, allocator<_Tp>&) { return std::__uninitialized_default_n(__first, __n); } template<typename _ForwardIterator, typename _Size> _GLIBCXX20_CONSTEXPR inline _ForwardIterator __uninitialized_default_n(_ForwardIterator __first, _Size __n) { typedef typename iterator_traits<_ForwardIterator>::value_type _ValueType; // See uninitialized_fill_n for the conditions for using std::fill_n. constexpr bool __can_fill = __and_<is_integral<_Size>, is_copy_assignable<_ValueType>>::value; return __uninitialized_default_n_1<__is_trivial(_ValueType) && __can_fill>:: __uninit_default_n(__first, __n); }
__uninitialized_default_n_1 尽可能调用 std::fill_n 处理。
template<bool _TrivialValueType> struct __uninitialized_default_n_1 { template<typename _ForwardIterator, typename _Size> _GLIBCXX20_CONSTEXPR static _ForwardIterator __uninit_default_n(_ForwardIterator __first, _Size __n) { _ForwardIterator __cur = __first; __try { for (; __n > 0; --__n, (void) ++__cur) std::_Construct(std::__addressof(*__cur)); return __cur; } __catch(...) { std::_Destroy(__first, __cur); __throw_exception_again; } } }; template<> struct __uninitialized_default_n_1<true> { template<typename _ForwardIterator, typename _Size> _GLIBCXX20_CONSTEXPR static _ForwardIterator __uninit_default_n(_ForwardIterator __first, _Size __n) { if (__n > 0) { typename iterator_traits<_ForwardIterator>::value_type* __val = std::__addressof(*__first); std::_Construct(__val); // 构造 first ++__first; // 剩余元素拷贝 __first = std::fill_n(__first, __n - 1, *__val); } return __first; } };
2.3.2、vector(n, val)
当构造函数指定元素个数和值时,最终 std::uninitialized_fill_n() 会被调用,初始化对象的值。
_GLIBCXX20_CONSTEXPR vector(size_type __n, const value_type& __value, const allocator_type& __a = allocator_type()) : _Base(_S_check_init_len(__n, __a), __a) { _M_fill_initialize(__n, __value); } _GLIBCXX20_CONSTEXPR void _M_fill_initialize(size_type __n, const value_type& __value) { this->_M_impl._M_finish = std::__uninitialized_fill_n_a(this->_M_impl._M_start, __n, __value, _M_get_Tp_allocator()); } template<typename _ForwardIterator, typename _Size, typename _Tp, typename _Tp2> _GLIBCXX20_CONSTEXPR inline _ForwardIterator __uninitialized_fill_n_a(_ForwardIterator __first, _Size __n, const _Tp& __x, allocator<_Tp2>&) { #ifdef __cpp_lib_is_constant_evaluated /* ... */ #endif return std::uninitialized_fill_n(__first, __n, __x); }
2.3.3、vector(first, last)
template<typename _InputIterator, typename = std::_RequireInputIter<_InputIterator>> _GLIBCXX20_CONSTEXPR vector(_InputIterator __first, _InputIterator __last, const allocator_type& __a = allocator_type()) : _Base(__a) { _M_range_initialize(__first, __last, std::__iterator_category(__first)); } template<typename _InputIterator> _GLIBCXX20_CONSTEXPR void _M_range_initialize(_InputIterator __first, _InputIterator __last, std::input_iterator_tag) { __try { for (; __first != __last; ++__first) #if __cplusplus >= 201103L emplace_back(*__first); // 逐一执行 emplace_back #else #endif } __catch(...) { clear(); __throw_exception_again; } } template<typename _ForwardIterator> _GLIBCXX20_CONSTEXPR void _M_range_initialize(_ForwardIterator __first, _ForwardIterator __last, std::forward_iterator_tag) { const size_type __n = std::distance(__first, __last); this->_M_impl._M_start = this->_M_allocate(_S_check_init_len(__n, _M_get_Tp_allocator())); this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n; this->_M_impl._M_finish = std::__uninitialized_copy_a(__first, __last, this->_M_impl._M_start, _M_get_Tp_allocator()); // 拷贝元素 }
2.4、vector::dctor
vector 的析构函数调用 std::_Destroy() 函数将 [start, finish) 范围的对象析构。std::_Destroy() 根据对象类型做优化:如果对象的析构函数是 default 或者编译器自动生成的,std::_Destroy() 什么都不做,否则依次调用对象的析构函数。
_GLIBCXX20_CONSTEXPR ~vector() _GLIBCXX_NOEXCEPT { std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, _M_get_Tp_allocator()); _GLIBCXX_ASAN_ANNOTATE_BEFORE_DEALLOC; }
2.5、push_back
如果有剩余空间,在末尾的位置构造一个对象
void push_back(const value_type& __x) { if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage) { _GLIBCXX_ASAN_ANNOTATE_GROW(1); _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, __x); ++this->_M_impl._M_finish; _GLIBCXX_ASAN_ANNOTATE_GREW(1); } else _M_realloc_insert(end(), __x); }
否则调用 _M_realloc_insert() 进行扩容
- 调用 _M_check_len() 函数计算新的长度
- 在新的内存地址构造新插入的元素
- 将就的元素拷贝到新内存
- 旧元素析构,旧内存释放
在元素构造和拷贝过程中,如果有任何异常抛出,已经构造的对象将被析构。
#if __cplusplus >= 201103L template<typename _Tp, typename _Alloc> template<typename... _Args> _GLIBCXX20_CONSTEXPR void vector<_Tp, _Alloc>:: _M_realloc_insert(iterator __position, _Args&&... __args) #else /* ... */ #endif { const size_type __len = _M_check_len(size_type(1), "vector::_M_realloc_insert"); pointer __old_start = this->_M_impl._M_start; pointer __old_finish = this->_M_impl._M_finish; const size_type __elems_before = __position - begin(); pointer __new_start(this->_M_allocate(__len)); // 扩容 pointer __new_finish(__new_start); __try { // The order of the three operations is dictated by the C++11 // case, where the moves could alter a new element belonging // to the existing vector. This is an issue only for callers // taking the element by lvalue ref (see last bullet of C++11 // [res.on.arguments]). _Alloc_traits::construct(this->_M_impl, __new_start + __elems_before, #if __cplusplus >= 201103L std::forward<_Args>(__args)...); // 构造插入元素 #else __x); #endif __new_finish = pointer(); #if __cplusplus >= 201103L if _GLIBCXX17_CONSTEXPR (_S_use_relocate()) { __new_finish = _S_relocate(__old_start, __position.base(), __new_start, _M_get_Tp_allocator()); // 拷贝旧元素 ++__new_finish; __new_finish = _S_relocate(__position.base(), __old_finish, __new_finish, _M_get_Tp_allocator()); // 拷贝旧元素 } else #endif { __new_finish = std::__uninitialized_move_if_noexcept_a (__old_start, __position.base(), __new_start, _M_get_Tp_allocator()); ++__new_finish; __new_finish = std::__uninitialized_move_if_noexcept_a (__position.base(), __old_finish, __new_finish, _M_get_Tp_allocator()); } } __catch(...) // 出现异常,析构对象,释放内存 { if (!__new_finish) _Alloc_traits::destroy(this->_M_impl, __new_start + __elems_before); else std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator()); _M_deallocate(__new_start, __len); __throw_exception_again; } #if __cplusplus >= 201103L if _GLIBCXX17_CONSTEXPR (!_S_use_relocate()) #endif std::_Destroy(__old_start, __old_finish, _M_get_Tp_allocator()); _GLIBCXX_ASAN_ANNOTATE_REINIT; _M_deallocate(__old_start, this->_M_impl._M_end_of_storage - __old_start); this->_M_impl._M_start = __new_start; this->_M_impl._M_finish = __new_finish; this->_M_impl._M_end_of_storage = __new_start + __len; }
_M_check_len() 函数可以看到,新的 size 时原来的两倍
_GLIBCXX20_CONSTEXPR size_type _M_check_len(size_type __n, const char* __s) const { if (max_size() - size() < __n) __throw_length_error(__N(__s)); const size_type __len = size() + (std::max)(size(), __n); return (__len < size() || __len > max_size()) ? max_size() : __len; }
emplace_back() 和 push_back() 类似,只不过 emplace_back() 调用的构造函数,push_back() 调用赋值构造函数。
#if __cplusplus >= 201103L template<typename _Tp, typename _Alloc> template<typename... _Args> #if __cplusplus > 201402L _GLIBCXX20_CONSTEXPR typename vector<_Tp, _Alloc>::reference #else void #endif vector<_Tp, _Alloc>:: emplace_back(_Args&&... __args) { if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage) { _GLIBCXX_ASAN_ANNOTATE_GROW(1); _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, std::forward<_Args>(__args)...); ++this->_M_impl._M_finish; _GLIBCXX_ASAN_ANNOTATE_GREW(1); } else _M_realloc_insert(end(), std::forward<_Args>(__args)...); #if __cplusplus > 201402L return back(); #endif } #endif
2.6、pop_back()
_GLIBCXX20_CONSTEXPR void pop_back() _GLIBCXX_NOEXCEPT { __glibcxx_requires_nonempty(); --this->_M_impl._M_finish; _Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish); _GLIBCXX_ASAN_ANNOTATE_SHRINK(1); }
2.7、insert()
vector::insert() 函数有多种类型,如果剩余空间不能够存放插入元素,需要扩容后。
2.7.1、insert(pos, val) / vector::insert(pos, rval)
insert() 函数接受左值和右值参数,两者处理逻辑相同。
- 还有剩余空间末尾插入,在末尾构造中间插入,调用 _M_insert_aux() 函数处理
- _M_realloc_insert() 扩容插入
插入的元素可能是 vector 中已有的元素,而 _M_insert_aux() 内部会移动元素(如果元素支持移动语义操作,使用移动语义),所以当传入参数是左值时,为了放置移动后元素失效,需要构造一个临时变量,当作拷贝。
template<typename _Tp, typename _Alloc> _GLIBCXX20_CONSTEXPR typename vector<_Tp, _Alloc>::iterator vector<_Tp, _Alloc>:: #if __cplusplus >= 201103L insert(const_iterator __position, const value_type& __x) #else /* ... */ #endif { const size_type __n = __position - begin(); if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage) if (__position == end()) // 插入位置在末尾,直接构造 { _GLIBCXX_ASAN_ANNOTATE_GROW(1); _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, __x); ++this->_M_impl._M_finish; _GLIBCXX_ASAN_ANNOTATE_GREW(1); } else { #if __cplusplus >= 201103L const auto __pos = begin() + (__position - cbegin()); // __x could be an existing element of this vector, so make a // copy of it before _M_insert_aux moves elements around. _Temporary_value __x_copy(this, __x); // 构造一个临时变量 _M_insert_aux(__pos, std::move(__x_copy._M_val())); // 移动后插入 #else /* ... */ #endif } else #if __cplusplus >= 201103L _M_realloc_insert(begin() + (__position - cbegin()), __x); // 扩容 #else /* ... */ #endif return iterator(this->_M_impl._M_start + __n); }
传入 insert() 函数的是右值,和传入左值的 insert() 逻辑相同
_GLIBCXX20_CONSTEXPR iterator insert(const_iterator __position, value_type&& __x) { return _M_insert_rval(__position, std::move(__x)); } template<typename _Tp, typename _Alloc> _GLIBCXX20_CONSTEXPR auto vector<_Tp, _Alloc>:: _M_insert_rval(const_iterator __position, value_type&& __v) -> iterator { const auto __n = __position - cbegin(); if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage) if (__position == cend()) { _GLIBCXX_ASAN_ANNOTATE_GROW(1); _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, std::move(__v)); ++this->_M_impl._M_finish; _GLIBCXX_ASAN_ANNOTATE_GREW(1); } else _M_insert_aux(begin() + __n, std::move(__v)); else _M_realloc_insert(begin() + __n, std::move(__v)); return iterator(this->_M_impl._M_start + __n); }
_M_insert_aux() 函数首先将末尾元素移动,然后再为插入元素腾地方,最后插入元素。
template<typename _Tp, typename _Alloc> template<typename _Arg> _GLIBCXX20_CONSTEXPR void vector<_Tp, _Alloc>:: _M_insert_aux(iterator __position, _Arg&& __arg) { _GLIBCXX_ASAN_ANNOTATE_GROW(1); _Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, _GLIBCXX_MOVE(*(this->_M_impl._M_finish - 1))); // 构造末尾元素 ++this->_M_impl._M_finish; _GLIBCXX_ASAN_ANNOTATE_GREW(1); #if __cplusplus < 201103L /* ... */ #endif // 将 [pos, finish - 2) 移动到 [pos + 1, finish - 1) _GLIBCXX_MOVE_BACKWARD3(__position.base(), this->_M_impl._M_finish - 2, this->_M_impl._M_finish - 1); #if __cplusplus < 201103L *__position = __x_copy; // 插入元素 #else /* ... */ #endif }
_M_realloc_insert() 的逻辑如下
- 2 被扩容
- 在插入位置出构造插入元素
- 移动原 vector 的元素
- 析构原 vector 元素,释放原 vector 内存
如果在构造或者移动元素过程中出现异常,新内存上所有已经构造的函数都会被析构,内存也被释放。
2.7.2、insert(pos, n, val)
_GLIBCXX20_CONSTEXPR iterator insert(const_iterator __position, size_type __n, const value_type& __x) { difference_type __offset = __position - cbegin(); _M_fill_insert(begin() + __offset, __n, __x); return begin() + __offset; }
_M_fill_insert() 定义如下,逻辑大致和 _M_realloc_insert() 相同
template<typename _Tp, typename _Alloc> _GLIBCXX20_CONSTEXPR void vector<_Tp, _Alloc>:: _M_fill_insert(iterator __position, size_type __n, const value_type& __x) { if (__n != 0) { if (size_type(this->_M_impl._M_end_of_storage - this->_M_impl._M_finish) >= __n) { // 剩余空间能够存放 n 数据 #if __cplusplus < 201103L value_type __x_copy = __x; #else _Temporary_value __tmp(this, __x); value_type& __x_copy = __tmp._M_val(); #endif const size_type __elems_after = end() - __position; pointer __old_finish(this->_M_impl._M_finish); if (__elems_after > __n) // initialized 空间大于 n { _GLIBCXX_ASAN_ANNOTATE_GROW(__n); std::__uninitialized_move_a(this->_M_impl._M_finish - __n, this->_M_impl._M_finish, this->_M_impl._M_finish, _M_get_Tp_allocator()); // 腾出位置 this->_M_impl._M_finish += __n; _GLIBCXX_ASAN_ANNOTATE_GREW(__n); _GLIBCXX_MOVE_BACKWARD3(__position.base(), __old_finish - __n, __old_finish); std::fill(__position.base(), __position.base() + __n, __x_copy); // 填充插入元素 } else // initialized 空间小于 n { _GLIBCXX_ASAN_ANNOTATE_GROW(__n); this->_M_impl._M_finish = std::__uninitialized_fill_n_a(this->_M_impl._M_finish, __n - __elems_after, __x_copy, _M_get_Tp_allocator()); // 在内存上构造一部分 _GLIBCXX_ASAN_ANNOTATE_GREW(__n - __elems_after); std::__uninitialized_move_a(__position.base(), __old_finish, this->_M_impl._M_finish, _M_get_Tp_allocator()); // 腾位置 this->_M_impl._M_finish += __elems_after; _GLIBCXX_ASAN_ANNOTATE_GREW(__elems_after); std::fill(__position.base(), __old_finish, __x_copy); // 填充数据 } } else // 扩容 { const size_type __len = _M_check_len(__n, "vector::_M_fill_insert"); const size_type __elems_before = __position - begin(); pointer __new_start(this->_M_allocate(__len)); pointer __new_finish(__new_start); __try { // See _M_realloc_insert above. std::__uninitialized_fill_n_a(__new_start + __elems_before, __n, __x, _M_get_Tp_allocator()); // 插入元素 __new_finish = pointer(); __new_finish = std::__uninitialized_move_if_noexcept_a (this->_M_impl._M_start, __position.base(), __new_start, _M_get_Tp_allocator()); // 拷贝原来的元素 __new_finish += __n; __new_finish = std::__uninitialized_move_if_noexcept_a (__position.base(), this->_M_impl._M_finish, __new_finish, _M_get_Tp_allocator()); // 拷贝原来的元素 } __catch(...) // 出现异常,析构对象,释放内存 { if (!__new_finish) std::_Destroy(__new_start + __elems_before, __new_start + __elems_before + __n, _M_get_Tp_allocator()); else std::_Destroy(__new_start, __new_finish, _M_get_Tp_allocator()); _M_deallocate(__new_start, __len); __throw_exception_again; } std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, _M_get_Tp_allocator()); _GLIBCXX_ASAN_ANNOTATE_REINIT; _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start); this->_M_impl._M_start = __new_start; this->_M_impl._M_finish = __new_finish; this->_M_impl._M_end_of_storage = __new_start + __len; } } }
欢迎关注公众号“源知源为”,阅读更多技术干货
#C++##STL##vector#C/C++ 语言基础