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++ 语言基础
平安产险科技中心工作强度 24人发布
