STL源码分析--vector
1. 简介
STL的容器分为序列式容器和关联式容器。vector作为序列式容器的一种,其内存模型与操作方式与数组类似,都是以一段连续的内存空间存储数据单元,区别在于数组的内存空间大小固定,不可变动,而vector可实现动态的内存迁移。当vector存储满后,会自动重新申请更大的内存空间作为新的存储容器,把旧的数据原样迁移到新的内存空间,此操作对用户层是无感知的。
用户层使用vector容器需包含头文件vector,方式为#include
STL对容器均使用模板类来实现,同时以空间分配器allocator作为内存管理者,管理着容器内存空间的申请和释放、数据对象的申请和回收,构造和析构。
vector的类图描述如下,其中的接口细节不完全展示出来。
-
allocator:空间分配器,管理内存的申请释放、数据对象的申请和回收,构造和析构。vector使用的空间分配器默认为std::new_alloctor;
-
_Vector_impl_data:维护容器的内存空间管理,三个指针_M_start指向内存的起始地址,_M_end_of_storage指向内存的末端地址,_M_finish指向容器最后一个元素的下一个地址空间。该类同时还提供了内存空间交换的接口,本质是交换指针。
-
_Vector_impl:继承自allocator和_Vector_Impl_data,同时该类内部还声明了_Asan模板类。
-
_Asan:GCC提供的一种内存错误检查器,这东西在编译和运行时使用,具体实现不在本博客说明。
-
_Vector_base:vector的基类。依赖_Vector_impl实现容器内存的构建、销毁。
-
vector:容器的具体实现,提供了插入、删除、定位等数据操作。
2. _Vector_impl_data的实现
struct _Vector_impl_data
{
pointer _M_start;
pointer _M_finish;
pointer _M_end_of_storage;
_GLIBCXX20_CONSTEXPR
_Vector_impl_data() _GLIBCXX_NOEXCEPT
: _M_start(), _M_finish(), _M_end_of_storage()
{ }
#if __cplusplus >= 201103L
// 移动构造函数
_GLIBCXX20_CONSTEXPR
_Vector_impl_data(_Vector_impl_data&& __x) noexcept
: _M_start(__x._M_start), _M_finish(__x._M_finish), _M_end_of_storage(__x._M_end_of_storage)
{ __x._M_start = __x._M_finish = __x._M_end_of_storage = pointer(); }
#endif
// 仅拷贝指针而不拷贝内存
_GLIBCXX20_CONSTEXPR
void _M_copy_data(_Vector_impl_data const& __x) _GLIBCXX_NOEXCEPT
{
_M_start = __x._M_start;
_M_finish = __x._M_finish;
_M_end_of_storage = __x._M_end_of_storage;
}
// 交换的是指针,而不是内存数据
_GLIBCXX20_CONSTEXPR
void _M_swap_data(_Vector_impl_data& __x) _GLIBCXX_NOEXCEPT
{
_Vector_impl_data __tmp;
__tmp._M_copy_data(*this);
_M_copy_data(__x);
__x._M_copy_data(__tmp);
}
};
3. _Vector_impl的实现
struct _Vector_impl : public _Tp_alloc_type, public _Vector_impl_data
{
_GLIBCXX20_CONSTEXPR
_Vector_impl() _GLIBCXX_NOEXCEPT_IF(is_nothrow_default_constructible<_Tp_alloc_type>::value)
: _Tp_alloc_type()
{ }
_GLIBCXX20_CONSTEXPR
_Vector_impl(_Tp_alloc_type const& __a) _GLIBCXX_NOEXCEPT
: _Tp_alloc_type(__a)
{ }
#if __cplusplus >= 201103L
_GLIBCXX20_CONSTEXPR
_Vector_impl(_Vector_impl&& __x) noexcept
: _Tp_alloc_type(std::move(__x)), _Vector_impl_data(std::move(__x))
{ }
_GLIBCXX20_CONSTEXPR
_Vector_impl(_Tp_alloc_type&& __a) noexcept
: _Tp_alloc_type(std::move(__a))
{ }
_GLIBCXX20_CONSTEXPR
_Vector_impl(_Tp_alloc_type&& __a, _Vector_impl&& __rv) noexcept
: _Tp_alloc_type(std::move(__a)), _Vector_impl_data(std::move(__rv))
{ }
#endif
#if _GLIBCXX_SANITIZE_STD_ALLOCATOR && _GLIBCXX_SANITIZE_VECTOR
template<typename = _Tp_alloc_type>
struct _Asan
{
......
};
template<typename _Up>
struct _Asan<allocator<_Up> >
{
......
};
};
4. _Vector_base的实现
template<typename _Tp, typename _Alloc>
struct _Vector_base
{
typedef typename __gnu_cxx::__alloc_traits<_Alloc>::template rebind<_Tp>::other _Tp_alloc_type;
typedef typename __gnu_cxx::__alloc_traits<_Tp_alloc_type>::pointer pointer;
struct _Vector_impl_data
{
// _Vector_impl_data的定义见第二节
};
struct _Vector_impl
: public _Tp_alloc_type, public _Vector_impl_data
{
// _Vector_impl的定义见第三节
};
public:
typedef _Alloc allocator_type;
// 获取空间分配器实例
_GLIBCXX20_CONSTEXPR
_Tp_alloc_type&
_M_get_Tp_allocator() _GLIBCXX_NOEXCEPT
{ return this->_M_impl; }
_GLIBCXX20_CONSTEXPR
const _Tp_alloc_type&
_M_get_Tp_allocator() const _GLIBCXX_NOEXCEPT
{ return this->_M_impl; }
_GLIBCXX20_CONSTEXPR
allocator_type
get_allocator() const _GLIBCXX_NOEXCEPT
{ return allocator_type(_M_get_Tp_allocator()); }
#if __cplusplus >= 201103L
_Vector_base() = default;
#else
_Vector_base() { }
#endif
_GLIBCXX20_CONSTEXPR
_Vector_base(const allocator_type& __a) _GLIBCXX_NOEXCEPT
: _M_impl(__a) { }
#if !_GLIBCXX_INLINE_VERSION
_GLIBCXX20_CONSTEXPR
_Vector_base(size_t __n)
: _M_impl()
{ _M_create_storage(__n); }
#endif
_GLIBCXX20_CONSTEXPR
_Vector_base(size_t __n, const allocator_type& __a)
: _M_impl(__a)
{ _M_create_storage(__n); }
#if __cplusplus >= 201103L
_Vector_base(_Vector_base&&) = default;
# if !_GLIBCXX_INLINE_VERSION
// 移动构造函数
_GLIBCXX20_CONSTEXPR
_Vector_base(_Tp_alloc_type&& __a) noexcept
: _M_impl(std::move(__a)) { }
// __x为要移动的_Vector_base对象,__a为要拷贝的空间分配器
_GLIBCXX20_CONSTEXPR
_Vector_base(_Vector_base&& __x, const allocator_type& __a)
: _M_impl(__a)
{
// 当指定的分配器__a与__x的分配器相等时,直接交换__x的指针指向即可,新的容器使用的内存空间仍为__x的内存空间;
// 当指定的分配器__a与__x的分配器不相等时,重新申请新的内存空间给新容器使用。
// 分配器的==运算符重载见allocator的实现,其始终返回true
if (__x.get_allocator() == __a)
this->_M_impl._M_swap_data(__x._M_impl);
else
{
size_t __n = __x._M_impl._M_finish - __x._M_impl._M_start;
_M_create_storage(__n);
}
}
# endif
_GLIBCXX20_CONSTEXPR
_Vector_base(const allocator_type& __a, _Vector_base&& __x)
: _M_impl(_Tp_alloc_type(__a), std::move(__x._M_impl))
{ }
#endif
_GLIBCXX20_CONSTEXPR
~_Vector_base() _GLIBCXX_NOEXCEPT
{
_M_deallocate(_M_impl._M_start, _M_impl._M_end_of_storage - _M_impl._M_start);
}
public:
_Vector_impl _M_impl;
// 构建容器的内存空间,空间大小为__n * sizeof(_Tp)
_GLIBCXX20_CONSTEXPR
pointer
_M_allocate(size_t __n)
{
// 通过萃取机制取出空间分配器,再调用分配器提供的allocate接口申请内存空间
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:
// 构建容器的内存空间,空间大小为__n * sizeof(_Tp), 同时初始化三个指针
// _M_start指向内存首地址,_M_end_of_storage指向内存末端地址,_M_finish初始等于_M_start
_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;
}
};
5. vector的实现
5.1 vector内的定义的类型别名
template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
class vector : protected _Vector_base<_Tp, _Alloc>
{
#ifdef _GLIBCXX_CONCEPT_CHECKS
// Concept requirements.
typedef typename _Alloc::value_type _Alloc_value_type;
# if __cplusplus < 201103L
__glibcxx_class_requires(_Tp, _SGIAssignableConcept)
# endif
__glibcxx_class_requires2(_Tp, _Alloc_value_type, _SameTypeConcept)
#endif
#if __cplusplus >= 201103L
static_assert(is_same<typename remove_cv<_Tp>::type, _Tp>::value,
"std::vector must have a non-const, non-volatile value_type");
# if __cplusplus > 201703L || defined __STRICT_ANSI__
static_assert(is_same<typename _Alloc::value_type, _Tp>::value,
"std::vector must have the same value_type as its allocator");
# endif
#endif
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;
protected:
using _Base::_M_allocate;
using _Base::_M_deallocate;
using _Base::_M_impl;
using _Base::_M_get_Tp_allocator;
......
};
5.2 vector的构造和析构
public:
#if __cplusplus >= 201103L
vector() = default;
#else
vector() { }
#endif
explicit _GLIBCXX20_CONSTEXPR
vector(const allocator_type& __a) _GLIBCXX_NOEXCEPT
: _Base(__a) { }
#if __cplusplus >= 201103L
// 初始化n个数据项,数据项的初始化采用默认构造。可指定空间分配器
explicit _GLIBCXX20_CONSTEXPR
vector(size_type __n, const allocator_type& __a = allocator_type())
: _Base(_S_check_init_len(__n, __a), __a)
{ _M_default_initialize(__n); }
// 初始化n个数据项,数据项的初始化为__value。可指定空间分配器
_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); }
#else
// 初始化n个数据项,数据项的初始化为__value。可指定空间分配器
explicit
vector(size_type __n, const value_type& __value = value_type(), const allocator_type& __a = allocator_type())
: _Base(_S_check_init_len(__n, __a), __a)
{ _M_fill_initialize(__n, __value); }
#endif
// 拷贝构造函数,将_x的所有数据项拷贝到新的容器内。_S_select_on_copy返回一份分配器的拷贝
_GLIBCXX20_CONSTEXPR
vector(const vector& __x)
: _Base(__x.size(), _Alloc_traits::_S_select_on_copy(__x._M_get_Tp_allocator()))
{
this->_M_impl._M_finish = std::__uninitialized_copy_a(__x.begin(), __x.end(),
this->_M_impl._M_start, _M_get_Tp_allocator());
}
#if __cplusplus >= 201103L
vector(vector&&) noexcept = default;
// 拷贝构造函数,将_x的所有数据项拷贝到新的容器内。
// __type_identity_t 能用于在模板实参推导中建立非推导语境:
_GLIBCXX20_CONSTEXPR
vector(const vector& __x, const __type_identity_t<allocator_type>& __a)
: _Base(__x.size(), __a)
{
this->_M_impl._M_finish = std::__uninitialized_copy_a(__x.begin(), __x.end(),
this->_M_impl._M_start, _M_get_Tp_allocator());
}
private:
// typename _Alloc_traits::is_always_equal{} 为true_type时
_GLIBCXX20_CONSTEXPR
vector(vector&& __rv, const allocator_type& __m, true_type) noexcept
: _Base(__m, std::move(__rv))
{ }
// typename _Alloc_traits::is_always_equal{} 为false_type时
_GLIBCXX20_CONSTEXPR
vector(vector&& __rv, const allocator_type& __m, false_type)
: _Base(__m)
{
// 当指定的分配器__m与__rv的分配器相等时,直接交换__rv的指针指向即可,新的容器使用的内存空间仍为__rv的内存空间;
// 当指定的分配器__m与__rv的分配器不相等时,重新申请新的内存空间给新容器使用。
// 分配器的==运算符重载见allocator的实现,其始终返回true
if (__rv.get_allocator() == __m)
this->_M_impl._M_swap_data(__rv._M_impl);
else if (!__rv.empty())
{
this->_M_create_storage(__rv.size());
this->_M_impl._M_finish = std::__uninitialized_move_a(__rv.begin(), __rv.end(),
this->_M_impl._M_start, _M_get_Tp_allocator());
__rv.clear();
}
}
public:
// 移动构造函数,采用C++11的委托构造方式,调用其他构造函数
_GLIBCXX20_CONSTEXPR
vector(vector&& __rv, const __type_identity_t<allocator_type>& __m)
noexcept( noexcept(vector(std::declval<vector&&>(), std::declval<const allocator_type&>(),
std::declval<typename _Alloc_traits::is_always_equal>())) )
: vector(std::move(__rv), __m, typename _Alloc_traits::is_always_equal{})
{ }
// 根据初始化列表创建容器,最终会调用数据项的n次拷贝构造函数来初始化对象。n为初始化列表的size大小
_GLIBCXX20_CONSTEXPR
vector(initializer_list<value_type> __l, const allocator_type& __a = allocator_type())
: _Base(__a)
{
_M_range_initialize(__l.begin(), __l.end(), random_access_iterator_tag());
}
#endif
// 根据迭代器范围初始化容器,会将[first,last)范围内的数据拷贝到新容器内。
// 如果迭代器类型是forward, bidirectional或random-access,将会调用数据项的N次拷贝构造,N为迭代器范围内的数据项数目。并且不会进行内存的重新分配(reallocate)
// 如果迭代器类型是input,会进行最多2N次的拷贝构造,和logN次的内存重新分配(reallocate)
#if __cplusplus >= 201103L
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));
}
#else
// 根据迭代器范围初始化容器,会将[first,last)范围内的数据拷贝到新容器内。
// std::__is_integer模板用来判断类型是否为整型,
// 当为true时,会调用_M_fill_initialize()进行初始化,__first为数据项个数,__last为初始化的值;
// 当为false时,会调用_M_range_initialize进行初始化,__first、__last均为迭代器。
template<typename _InputIterator>
vector(_InputIterator __first, _InputIterator __last, const allocator_type& __a = allocator_type())
: _Base(__a)
{
typedef typename std::__is_integer<_InputIterator>::__type _Integral;
_M_initialize_dispatch(__first, __last, _Integral());
}
#endif
// 析构函数仅从容器内删除数据项,如果数据项是指针,析构函数不会释放指针所指的内存空间,用户层应该在析构前主动释放内存
_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;
}
构造函数调用的几个函数接口介绍:
static _GLIBCXX20_CONSTEXPR size_type
_S_max_size(const _Tp_alloc_type& __a) _GLIBCXX_NOEXCEPT
{
// std::distance(begin(), end()) cannot be greater than PTRDIFF_MAX,
// and realistically we can't store more than PTRDIFF_MAX/sizeof(T)
// (even if std::allocator_traits::max_size says we can).
const size_t __diffmax = __gnu_cxx::__numeric_traits<ptrdiff_t>::__max / sizeof(_Tp);
const size_t __allocmax = _Alloc_traits::max_size(__a);
return (std::min)(__diffmax, __allocmax);
}
static _GLIBCXX20_CONSTEXPR size_type
_S_check_init_len(size_type __n, const allocator_type& __a)
{
if (__n > _S_max_size(_Tp_alloc_type(__a)))
__throw_length_error(__N("cannot create std::vector larger than max_size()"));
return __n;
}
#if __cplusplus >= 201103L
// 初始化n个数据项,调用的是数据项的默认构造函数
_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());
}
#endif
// 初始化n个数据项,值为__value,调用的是数据项的拷贝构造、赋值运算重载、或memset
_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());
}
// 仅当迭代器类型为input时调用
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);
#else
push_back(*__first);
#endif
} __catch(...) {
clear();
__throw_exception_again;
}
}
// 当迭代器类型为forward、bidirectional或random-access调用,会重新申请内存并进行数据项拷贝
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());
}
#if __cplusplus < 201103L
// _Integer 为整型类型
template<typename _Integer>
void _M_initialize_dispatch(_Integer __n, _Integer __value, __true_type)
{
this->_M_impl._M_start = _M_allocate(_S_check_init_len(
static_cast<size_type>(__n), _M_get_Tp_allocator()));
this->_M_impl._M_end_of_storage = this->_M_impl._M_start + static_cast<size_type>(__n);
_M_fill_initialize(static_cast<size_type>(__n), __value);
}
// _InputIterator为迭代器类型
template<typename _InputIterator>
void _M_initialize_dispatch(_InputIterator __first, _InputIterator __last, __false_type)
{
_M_range_initialize(__first, __last, std::__iterator_category(__first));
}
#endif
其中__uninitialized_default_n_a、__uninitialized_fill_n_a、__uninitialized_copy_a、__uninitialized_move_a几个函数是全局的内存处理函数,用于填充、拷贝、移动元素等处理,详细介绍见 STL源码分析--全局内存处理函数
几个函数模板介绍:
__type_identity_t 能用于在模板实参推导中建立非推导语境,示例如下:
template<class T>
void f(T, T);
template<class T>
void g(T, std::type_identity_t<T>);
f(4.2, 0); // 错误:对 'T' 推导出冲突的类型
g(4.2, 0); // OK :调用 g<double>
_Alloc_traits::is_always_equal 是用于得到空间分配器是否相等的特性,allocator的相等比较的意义是:一个allocator分配的空间,是否可以用另外一个allocator来释放。stl在<bits/allocator.h>定义了is_always_equal 模板始终为true_type。stl默认的空间分配器就是std::new_allocator,因为是new和delete的封装,一个std::allocator new的当然可以用另一个std::allocator来delete。
// 定义在<bits/allocator.h>中的allocator类模板中
using is_always_equal _GLIBCXX20_DEPRECATED_SUGGEST("std::allocator_traits::is_always_equal") = true_type;
// 以下均定义在<bits/alloc_traits.h>中
struct __allocator_traits_base
{
template<typename _Tp>
using __equal = typename _Tp::is_always_equal;
......
};
template<typename _Alloc>
struct allocator_traits : __allocator_traits_base
{
// __detected_or_t用来检查类型有效性,当__equal<_Alloc>,即 _Alloc::is_always_equal有效时,is_always_equal = _Alloc::is_always_equal
// 否则,is_always_equal = is_empty<_Alloc>::type
// is_empty 用来判断类型是否为空类型,空类型则返回true_type, 否则返回false_type
using is_always_equal = __detected_or_t<typename is_empty<_Alloc>::type, __equal, _Alloc>;
......
};
// 偏特化版本,分配器为allocator,即为std::new_allocator
template<typename _Tp>
struct allocator_traits<allocator<_Tp>>
{
using is_always_equal = true_type;
......
};
template<>
struct allocator_traits<allocator<void>>
{
using is_always_equal = true_type;
......
};
is_empty:如果T是空类型(即,除大小为0的位字段外,没有非静态数据成员、没有虚拟函数、没有虚拟基类和非空基类的非并集类类型),则提供等于true的成员常量值。对于任何其他类型,值都为false。
std::declval的功能:返回某个类型T的右值引用,不管该类型是否有默认构造函数或者该类型是否可以创建对象。
5.3 vector的迭代器
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;
vector定义了如上几种迭代器(其实为两种,每种各包含普通迭代器和常量迭代器),根据迭代器元素的遍历方向分为正向迭代器和反向迭代器。vector迭代器的本质为原生指针,属于Random Access Iterator,分别通过模板__normal_iterator和reverse_iterator做简单封装,实现指针的所有算数操作。__normal_iterator为正向迭代器,reverse_iterator为反向迭代器。关于迭代器的说明见 STL源码分析--迭代器iterator
5.4 vector的定界操作
// 返回指向容器第一个元素的迭代器(可读可写),迭代器是正向遍历的
_GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR
iterator begin() _GLIBCXX_NOEXCEPT
{ return iterator(this->_M_impl._M_start); }
// 返回指向容器第一个元素的迭代器(只读),迭代器是正向遍历的
_GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR
const_iterator begin() const _GLIBCXX_NOEXCEPT
{ return const_iterator(this->_M_impl._M_start); }
// 返回指向容器最后一个元素下一个位置的迭代器(可读可写),迭代器是正向遍历的
_GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR
iterator end() _GLIBCXX_NOEXCEPT
{ return iterator(this->_M_impl._M_finish); }
// 返回指向容器最后一个元素下一个位置的迭代器(只读),迭代器是正向遍历的
_GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR
const_iterator end() const _GLIBCXX_NOEXCEPT
{ return const_iterator(this->_M_impl._M_finish); }
// 返回指向容器最后一个元素的迭代器(可读可写),迭代器是反向遍历的
_GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR
reverse_iterator rbegin() _GLIBCXX_NOEXCEPT
{ return reverse_iterator(end()); }
// 返回指向容器最后一个元素的迭代器(只读),迭代器是反向遍历的
_GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR
const_reverse_iterator rbegin() const _GLIBCXX_NOEXCEPT
{ return const_reverse_iterator(end()); }
// 返回指向容器第一个元素前一个位置的迭代器(可读可写),迭代器是反向遍历的
_GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR
reverse_iterator rend() _GLIBCXX_NOEXCEPT
{ return reverse_iterator(begin()); }
// 返回指向容器第一个元素前一个位置的迭代器(只读),迭代器是反向遍历的
_GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR
const_reverse_iterator rend() const _GLIBCXX_NOEXCEPT
{ return const_reverse_iterator(begin()); }
#if __cplusplus >= 201103L
// 返回指向容器第一个元素的迭代器(只读),迭代器是正向遍历的
[[__nodiscard__]] _GLIBCXX20_CONSTEXPR
const_iterator cbegin() const noexcept
{ return const_iterator(this->_M_impl._M_start); }
// 返回指向容器最后一个元素下一个位置的迭代器(只读),迭代器是正向遍历的
[[__nodiscard__]] _GLIBCXX20_CONSTEXPR
const_iterator cend() const noexcept
{ return const_iterator(this->_M_impl._M_finish); }
// 返回指向容器最后一个元素的迭代器(只读),迭代器是反向遍历的
[[__nodiscard__]] _GLIBCXX20_CONSTEXPR
const_reverse_iterator crbegin() const noexcept
{ return const_reverse_iterator(end()); }
// 返回指向容器第一个元素前一个位置的迭代器(只读),迭代器是反向遍历的
[[__nodiscard__]] _GLIBCXX20_CONSTEXPR
const_reverse_iterator crend() const noexcept
{ return const_reverse_iterator(begin()); }
#endif
// 返回容器的第一个元素的引用
_GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR
reference front() _GLIBCXX_NOEXCEPT
{
__glibcxx_requires_nonempty();
return *begin();
}
// 返回容器的第一个元素的常量引用
_GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR
const_reference front() const _GLIBCXX_NOEXCEPT
{
__glibcxx_requires_nonempty();
return *begin();
}
// 返回容器最后一个元素的引用
_GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR
reference back() _GLIBCXX_NOEXCEPT
{
__glibcxx_requires_nonempty();
return *(end() - 1);
}
// 返回容器最后一个元素的常量引用
_GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR
const_reference back() const _GLIBCXX_NOEXCEPT
{
__glibcxx_requires_nonempty();
return *(end() - 1);
}
// 返回容器第一个元素的地址
_GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR
_Tp* data() _GLIBCXX_NOEXCEPT
{ return _M_data_ptr(this->_M_impl._M_start); }
_GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR
const _Tp* data() const _GLIBCXX_NOEXCEPT
{ return _M_data_ptr(this->_M_impl._M_start); }
5.5 vector的插入和删除
5.5.1 删除
先来看下删除,删除的方式有几种,erase、pop_back以及clear,对外提供的接口有:
#if __cplusplus >= 201103L
iterator erase(const_iterator __position);
iterator erase(const_iterator __first, const_iterator __last);
#else
iterator erase(iterator __position);
iterator erase(iterator __first, iterator __last);
#endif
void pop_back();
void clear();
接口的实现细节如下:
// 删除__pos后的所有元素,含__pos。该函数私有,被erase(q1,q2), clear(),
// resize(), _M_fill_assign,_M_assign_aux调用
_GLIBCXX20_CONSTEXPR
void _M_erase_at_end(pointer __pos) _GLIBCXX_NOEXCEPT
{
if (size_type __n = this->_M_impl._M_finish - __pos)
{
std::_Destroy(__pos, this->_M_impl._M_finish, _M_get_Tp_allocator());
this->_M_impl._M_finish = __pos;
_GLIBCXX_ASAN_ANNOTATE_SHRINK(__n);
}
}
// 删除容器__position指向的元素,返回的迭代器指向__position的下一元素(或 end())。__position删除后该迭代器失效
GLIBCXX20_CONSTEXPR
iterator
#if __cplusplus >= 201103L
erase(const_iterator __position)
{ return _M_erase(begin() + (__position - cbegin())); }
#else
erase(iterator __position)
{ return _M_erase(__position); }
#endif
// 删除容器[__first,__last)范围内的元素,返回的迭代器指向__last原先指向的元素(或end())
_GLIBCXX20_CONSTEXPR
iterator
#if __cplusplus >= 201103L
erase(const_iterator __first, const_iterator __last)
{
const auto __beg = begin();
const auto __cbeg = cbegin();
return _M_erase(__beg + (__first - __cbeg), __beg + (__last - __cbeg));
}
#else
erase(iterator __first, iterator __last)
{ return _M_erase(__first, __last); }
#endif
// 清空容器
_GLIBCXX20_CONSTEXPR
void clear() _GLIBCXX_NOEXCEPT
{ _M_erase_at_end(this->_M_impl._M_start); }
_GLIBCXX20_CONSTEXPR
iterator _M_erase(iterator __position);
_GLIBCXX20_CONSTEXPR
iterator _M_erase(iterator __first, iterator __last);
// 定义与bits/vector.tcc文件中
template<typename _Tp, typename _Alloc>
_GLIBCXX20_CONSTEXPR
typename vector<_Tp, _Alloc>::iterator
vector<_Tp, _Alloc>::
_M_erase(iterator __position)
{
// 从__position + 1起,到end(), 每个元素往前挪一个单位(拷贝或移动)
if (__position + 1 != end())
_GLIBCXX_MOVE3(__position + 1, end(), __position);
// 更新_M_finish,并删除最后一个元素
--this->_M_impl._M_finish;
_Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish);
_GLIBCXX_ASAN_ANNOTATE_SHRINK(1);
return __position;
}
template<typename _Tp, typename _Alloc>
_GLIBCXX20_CONSTEXPR
typename vector<_Tp, _Alloc>::iterator
vector<_Tp, _Alloc>::
_M_erase(iterator __first, iterator __last)
{
if (__first != __last)
{
// 将[__last, end())范围内的数据拷到__first所指的内存空间上
if (__last != end())
_GLIBCXX_MOVE3(__last, end(), __first);
// end() - __last 为拷贝或移动的数据项个数,所以__first.base() + (end() - __last)就是为最后要删除的起始地址
_M_erase_at_end(__first.base() + (end() - __last));
}
return __first;
}
// 定义于<bits/stl_algobase.h>
#if __cplusplus >= 201103L
#define _GLIBCXX_MOVE3(_Tp, _Up, _Vp) std::move(_Tp, _Up, _Vp)
#else
#define _GLIBCXX_MOVE3(_Tp, _Up, _Vp) std::copy(_Tp, _Up, _Vp)
#endif
// 删除末端的一个元素
_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);
}
iterator _M_erase(iterator __position); 操作的示例图如下:
iterator _M_erase(iterator __first, iterator __last);操作的示例图如下:
需要注意的是,当vector存储的数据是指针时,所有的删除操作(包括erase、pop_back和clear),都只会将数据项也就是指针从容器删除,而不会释放指针所指的内存空间。
5.5.2 插入
vector插入数据的方式有几种,push_back、assign、insert等 ,对外提供的接口有:
void push_back(const value_type& __x);
void assign(size_type __n, const value_type& __val);
#if __cplusplus >= 201103L
void push_back(value_type&& __x);
void emplace_back(_Args&&... __args);
iterator emplace(const_iterator __position, _Args&&... __args);
iterator insert(const_iterator __position, const value_type& __x);
iterator insert(const_iterator __position, value_type&& __x);
iterator insert(const_iterator __position, initializer_list<value_type> __l);
iterator insert(const_iterator __position, size_type __n, const value_type& __x);
iterator insert(const_iterator __position, _InputIterator __first, _InputIterator __last);
void assign(_InputIterator __first, _InputIterator __last);
void assign(initializer_list<value_type> __l);
#else
iterator insert(iterator __position, const value_type& __x);
void insert(iterator __position, size_type __n, const value_type& __x);
void insert(iterator __position, _InputIterator __first, _InputIterator __last);
void assign(_InputIterator __first, _InputIterator __last);
#endif
#if __cplusplus > 201402L
reference emplace_back(_Args&&... __args);
#endif
5.5.2.1 assign函数
_GLIBCXX20_CONSTEXPR
void assign(size_type __n, const value_type& __val)
{ _M_fill_assign(__n, __val); }
#if __cplusplus >= 201103L
template<typename _InputIterator,
typename = std::_RequireInputIter<_InputIterator>>
_GLIBCXX20_CONSTEXPR
void assign(_InputIterator __first, _InputIterator __last)
{ _M_assign_dispatch(__first, __last, __false_type()); }
#else
template<typename _InputIterator>
void assign(_InputIterator __first, _InputIterator __last)
{
// 检查_InputIterator类型是否为整型,非整型时,为迭代器
typedef typename std::__is_integer<_InputIterator>::__type _Integral;
_M_assign_dispatch(__first, __last, _Integral());
}
#endif
#if __cplusplus >= 201103L
// 支持初始化列表作为参数赋值
_GLIBCXX20_CONSTEXPR
void assign(initializer_list<value_type> __l)
{
this->_M_assign_aux(__l.begin(), __l.end(), random_access_iterator_tag());
}
#endif
// 类型_Integer为整型时调用
template<typename _Integer>
_GLIBCXX20_CONSTEXPR
void _M_assign_dispatch(_Integer __n, _Integer __val, __true_type)
{ _M_fill_assign(__n, __val); }
// 类型_InputIterator为迭代器类型时调用
template<typename _InputIterator>
_GLIBCXX20_CONSTEXPR
void _M_assign_dispatch(_InputIterator __first, _InputIterator __last, __false_type)
{ _M_assign_aux(__first, __last, std::__iterator_category(__first)); }
// 定义于bits/vector.tcc文件
template<typename _Tp, typename _Alloc>
_GLIBCXX20_CONSTEXPR
void vector<_Tp, _Alloc>::
_M_fill_assign(size_t __n, const value_type& __val)
{
// 如果__n大于容器容量,则需重新申请内存空间,此处创建一个临时的容器,并将其与旧容器交换指针,即交换容器的内存空间所有权
if (__n > capacity())
{
vector __tmp(__n, __val, _M_get_Tp_allocator());
__tmp._M_impl._M_swap_data(this->_M_impl);
}
// 如果__n大于现有容器数据个数,但小于容器容量,则先将原有的数据项修改为__val,后构造剩余的数据项。
else if (__n > size())
{
std::fill(begin(), end(), __val);
const size_type __add = __n - size();
_GLIBCXX_ASAN_ANNOTATE_GROW(__add);
this->_M_impl._M_finish = std::__uninitialized_fill_n_a(this->_M_impl._M_finish,
__add, __val, _M_get_Tp_allocator());
_GLIBCXX_ASAN_ANNOTATE_GREW(__add);
}
// 如果__n小于等于现有容器数据个数,则将容器前__n个数据项改为__val,后将多余的数据删除掉
else
_M_erase_at_end(std::fill_n(this->_M_impl._M_start, __n, __val));
}
// 仅当迭代器类型为input时调用
template<typename _Tp, typename _Alloc>
template<typename _InputIterator>
_GLIBCXX20_CONSTEXPR
void vector<_Tp, _Alloc>::
_M_assign_aux(_InputIterator __first, _InputIterator __last, std::input_iterator_tag)
{
pointer __cur(this->_M_impl._M_start);
// 将[__first, __last)元素赋值到[_M_start, _M_finish)区间
for (; __first != __last && __cur != this->_M_impl._M_finish; ++__cur, (void)++__first)
*__cur = *__first;
// 当[__first, __last)元素个数小于等于[_M_start, _M_finish)时,删除__cur及其后剩余的数据
if (__first == __last)
_M_erase_at_end(__cur);
// 当[__first, __last)元素个数大于[_M_start, _M_finish)时,需要继续[__first, __last)未赋值的元素填充到容器末端
else
_M_range_insert(end(), __first, __last, std::__iterator_category(__first));
}
// 当迭代器类型为forward、bidirectional或random-access调用
template<typename _Tp, typename _Alloc>
template<typename _ForwardIterator>
_GLIBCXX20_CONSTEXPR
void vector<_Tp, _Alloc>::
_M_assign_aux(_ForwardIterator __first, _ForwardIterator __last, std::forward_iterator_tag)
{
const size_type __len = std::distance(__first, __last);
// 当[__first, __last)元素个数大于原有容器容量,重新申请内存空间使用,并析构原容器内的所有元素、释放原容器内存空间
if (__len > capacity())
{
_S_check_init_len(__len, _M_get_Tp_allocator());
pointer __tmp(_M_allocate_and_copy(__len, __first, __last));
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 = __tmp;
this->_M_impl._M_finish = this->_M_impl._M_start + __len;
this->_M_impl._M_end_of_storage = this->_M_impl._M_finish;
}
// 当[__first, __last)元素个数小于等于容器容量,且小于容器数据项个数时,
// 将[__first, __last)元素赋值到容器内,并将剩余的旧数据项删除
else if (size() >= __len)
_M_erase_at_end(std::copy(__first, __last, this->_M_impl._M_start));
// 当[__first, __last)元素个数小于等于容器容量,大于等于容器数据项个数时,
// 先将[__first, __last)前size()个元素赋值到容器内,再将剩余未赋值的元素拷贝到容器末端
else
{
_ForwardIterator __mid = __first;
std::advance(__mid, size()); // 相当于__mid += size()
std::copy(__first, __mid, this->_M_impl._M_start);
const size_type __attribute__((__unused__)) __n = __len - size();
_GLIBCXX_ASAN_ANNOTATE_GROW(__n);
this->_M_impl._M_finish = std::__uninitialized_copy_a(__mid, __last, this->_M_impl._M_finish,
_M_get_Tp_allocator());
_GLIBCXX_ASAN_ANNOTATE_GREW(__n);
}
}
assign操作将不再保留原容器的数据,而是完全根据assign函数指定的参数重新填充容器,原有数据被丢弃。
其中调用的子接口_M_range_insert()实现的细节如下:
// 定义于bits/stl_iterator.h
#if __cplusplus >= 201103L
#define _GLIBCXX_MAKE_MOVE_ITERATOR(_Iter) std::make_move_iterator(_Iter)
#define _GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(_Iter) \
std::__make_move_if_noexcept_iterator(_Iter)
#else
#define _GLIBCXX_MAKE_MOVE_ITERATOR(_Iter) (_Iter)
#define _GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(_Iter) (_Iter)
#endif // C++11
// 定义于bits/stl_algobase.h
#if __cplusplus >= 201103L
#define _GLIBCXX_MOVE_BACKWARD3(_Tp, _Up, _Vp) std::move_backward(_Tp, _Up, _Vp)
#else
#define _GLIBCXX_MOVE_BACKWARD3(_Tp, _Up, _Vp) std::copy_backward(_Tp, _Up, _Vp)
#endif
// 仅当迭代器类型为input时调用当迭代器类型
template<typename _Tp, typename _Alloc>
template<typename _InputIterator>
_GLIBCXX20_CONSTEXPR
void vector<_Tp, _Alloc>::
_M_range_insert(iterator __pos, _InputIterator __first, _InputIterator __last, std::input_iterator_tag)
{
// 如果是容器尾端插入,则遍历[__fitst,__last),依次插入到容器
if (__pos == end())
{
for (; __first != __last; ++__first)
insert(end(), *__first);
}
// 如果是在容器中间或首部插入,先构建临时的容器,再一次将[__fitst,__last)插入容器,
// 此时只需对__pos后的元素做一次调整
else if (__first != __last)
{
vector __tmp(__first, __last, _M_get_Tp_allocator());
insert(__pos, _GLIBCXX_MAKE_MOVE_ITERATOR(__tmp.begin()),
_GLIBCXX_MAKE_MOVE_ITERATOR(__tmp.end()));
}
}
// 当迭代器类型为forward、bidirectional或random-access调用
template<typename _Tp, typename _Alloc>
template<typename _ForwardIterator>
_GLIBCXX20_CONSTEXPR
void vector<_Tp, _Alloc>::
_M_range_insert(iterator __position, _ForwardIterator __first, _ForwardIterator __last, std::forward_iterator_tag)
{
if (__first != __last)
{
const size_type __n = std::distance(__first, __last);
// 如果容器剩余空间足够插入
if (size_type(this->_M_impl._M_end_of_storage - this->_M_impl._M_finish) >= __n)
{
const size_type __elems_after = end() - __position;
pointer __old_finish(this->_M_impl._M_finish);
// __position到容器末端end,剩余元素个数__elems_after大于待插入的元素个数__n
if (__elems_after > __n)
{
_GLIBCXX_ASAN_ANNOTATE_GROW(__n);
// 先将容器最后__n个元素移动到容器末端,中间腾出__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());
// 更新_M_finish指针
this->_M_impl._M_finish += __n;
_GLIBCXX_ASAN_ANNOTATE_GREW(__n);
// 移动或拷贝[__position, __old_finish - __n)到[__old_finish - (__elems_after - __n), __old_finish)
_GLIBCXX_MOVE_BACKWARD3(__position.base(), __old_finish - __n, __old_finish);
// 拷贝[__first, __last)到[__position, __position + __n)
std::copy(__first, __last, __position);
}
// __position到容器末端end,剩余元素个数__elems_after小于等于待插入的元素个数__n
else
{
_ForwardIterator __mid = __first;
// 等同于__mid += elems_after
std::advance(__mid, __elems_after);
_GLIBCXX_ASAN_ANNOTATE_GROW(__n);
// 先将[__first+elems_after, __last)区间的数据拷贝到容器末端
std::__uninitialized_copy_a(__mid, __last, this->_M_impl._M_finish, _M_get_Tp_allocator());
// 更新_M_finish指针
this->_M_impl._M_finish += __n - __elems_after;
_GLIBCXX_ASAN_ANNOTATE_GREW(__n - __elems_after);
// 移动[__position, __old_finish)到容器末端
std::__uninitialized_move_a(__position.base(), __old_finish,
this->_M_impl._M_finish, _M_get_Tp_allocator());
// 更新_M_finish指针
this->_M_impl._M_finish += __elems_after;
_GLIBCXX_ASAN_ANNOTATE_GREW(__elems_after);
// 拷贝[__first, __first+elems_after)到[__position, __position+elems_after)
std::copy(__first, __mid, __position);
}
}
// 容器剩余空间不足
else
{
// 如果size() > __n,则容器新申请的空间大小为2倍size(), 否则为size()+__n
const size_type __len = _M_check_len(__n, "vector::_M_range_insert");
pointer __new_start(this->_M_allocate(__len));
pointer __new_finish(__new_start);
__try
{
// 移动[_M_start, __position)区间的数据到新的内存空间上
__new_finish = std::__uninitialized_move_if_noexcept_a
(this->_M_impl._M_start, __position.base(), __new_start, _M_get_Tp_allocator());
// 拷贝[__first, __last)区间的数据到容器末端
__new_finish = std::__uninitialized_copy_a(__first, __last,
__new_finish, _M_get_Tp_allocator());
// 移动[__position, _M_finish)区间的数据到容器末端
__new_finish = std::__uninitialized_move_if_noexcept_a(__position.base(),
this->_M_impl._M_finish, __new_finish, _M_get_Tp_allocator());
}
__catch(...)
{
// 若捕获异常,则析构所有元素,并释放申请的空间
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;
}
}
}
_GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR
size_type max_size() const _GLIBCXX_NOEXCEPT
{ return _S_max_size(_M_get_Tp_allocator()); }
_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;
}
_M_range_insert(iterator __position, _ForwardIterator __first, _ForwardIterator __last, std::forward_iterator_tag)负责将区间[__first, last)的数据插入到__position位置。
有如下几种情况:
1)容器剩余空间足够插入,从插入位置__position到容器末端end()的数据个数大于待插入的数据个数,操作步骤示意图如下:
假定,待插入个数__n = 2,__position到容器末端end()的数据个数__elems_after = 4
步骤1,先将容器末端最后__n个元素移动至末尾__M_finish处;
步骤2,将__position开始的__elems_after - _n个元素移动至原__M_finish前的__elems_after - _n个位置(std::move_back实现);
步骤3,将待插入的__n个元素拷贝到__position开始处(步骤1和2已经将__position开始的__n个元素做了往后挪位的操作)。
2)容器剩余空间足够插入,从插入位置__position到容器末端end()的数据个数小于等于待插入的数据个数,操作步骤示意图如下:
假定,待插入个数__n = 5,__position到容器末端end()的数据个数__elems_after = 2
步骤1,将[__first + elems_after, __last)的数据拷贝到容器末端;
步骤2,将__position开始到原容器末端(old_finish)的数据拷贝到现在的容器末端;
步骤3,将[__first, __first + elems_after)的数据拷贝到__position开始的位置。
3)容器剩余空间不足插入新元素,操作步骤示意图如下:
假定,待插入个数__n = 2,__position到容器末端end()的数据个数__elems_after = 2,剩余空间为1
步骤1,为容器重新申请内存空间,并将[_M_start, __position)的元素移动到新内存空间地址上。申请大小根据容器的元素个数size和待插入元素个数__n决定,当size > __n时,为2倍size,否则为size+__n;
步骤2,将待插入元素[__first, __last)插入到新容器末尾;
步骤3,将原[__position, _M_finish)区间的元素插入到新容器末尾;
步骤4,将原容器[__M_start, _M_finish)区间的元素析构,并释放原内存空间,同时将 __M_start、_M_finish、_M_end_of_storage指针指向新的内存地址。
5.5.2.2 push_back函数
push_back函数用于在容器末端插入元素,当容器未满时直接在末端插入,当容器满时调用_M_realloc_insert()重新申请内存空间并将原容器数据迁移,再于尾端插入元素。
_GLIBCXX20_CONSTEXPR
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);
}
#if __cplusplus >= 201103L
_GLIBCXX20_CONSTEXPR
void push_back(value_type&& __x)
{ emplace_back(std::move(__x)); }
template<typename... _Args>
#if __cplusplus > 201402L
_GLIBCXX20_CONSTEXPR
reference
#else
void
#endif
emplace_back(_Args&&... __args);
#endif
其中调用的子接口_M_realloc_insert()实现的细节如下:
#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
template<typename _Tp, typename _Alloc>
void vector<_Tp, _Alloc>::_M_realloc_insert(iterator __position, const _Tp& __x)
#endif
{
// _M_check_len计算并返回新的内存空间大小
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
{
// 在新内存空间的__position位置先构造 待插入的对象
_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
// _S_use_relocate判断_Alloc::value_type是否能移动插入(__is_move_insertable),std::__relocate_a调用是否会抛异常
// 如果支持移动插入,std::__relocate_a不抛异常,则返回true,最终通过std::__relocate_a移动元素
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());
}
// 否则使用std::__uninitialized_move_if_noexcept_a移动元素
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;
}
_S_use_relocate()函数用来判断是否可使用std::__relocate_a进行数据构造,它判断_Alloc::value_type是否能移动插入(__is_move_insertable),std::__relocate_a调用是否会抛异常;
_S_relocate()当满足_S_use_relocate()为常量表达式时(理解应该是函数的constexpr属性生效,_S_use_relocate()在编译期间计算结果,作为常量表达式),调用std::__relocate_a。
std::__relocate_a函数的具体实现在<bits/stl_uninitialized.h>中,此处不予分析。
#if __cplusplus >= 201103L
static constexpr bool _S_nothrow_relocate(true_type)
{
return noexcept(std::__relocate_a(std::declval<pointer>(),
std::declval<pointer>(),
std::declval<pointer>(),
std::declval<_Tp_alloc_type&>()));
}
static constexpr bool _S_nothrow_relocate(false_type)
{ return false; }
static constexpr bool _S_use_relocate()
{
// Instantiating std::__relocate_a might cause an error outside the
// immediate context (in __relocate_object_a's noexcept-specifier),
// so only do it if we know the type can be move-inserted into *this.
return _S_nothrow_relocate(__is_move_insertable<_Tp_alloc_type>{});
}
static pointer _S_do_relocate(pointer __first, pointer __last, pointer __result,
_Tp_alloc_type& __alloc, true_type) noexcept
{
return std::__relocate_a(__first, __last, __result, __alloc);
}
static pointer _S_do_relocate(pointer, pointer, pointer __result,
_Tp_alloc_type&, false_type) noexcept
{ return __result; }
static _GLIBCXX20_CONSTEXPR pointer
_S_relocate(pointer __first, pointer __last, pointer __result,
_Tp_alloc_type& __alloc) noexcept
{
#if __cpp_if_constexpr
// All callers have already checked _S_use_relocate() so just do it.
return std::__relocate_a(__first, __last, __result, __alloc);
#else
using __do_it = __bool_constant<_S_use_relocate()>;
return _S_do_relocate(__first, __last, __result, __alloc, __do_it{});
#endif
}
#endif // C++11
子接口emplace_back()的处理类同void push_back(const value_type& __x)
// 在vector.tcc 定义
#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
5.5.2.3 insert函数
1)insert(const_iterator __position, const value_type& __x),在指定的__position位置插入元素__x
// 在vector.tcc 定义
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
insert(iterator __position, const value_type& __x)
#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);
}
// 插入位置不在容器末端,调用_M_insert_aux在指定位置插入元素
else
{
#if __cplusplus >= 201103L
const auto __pos = begin() + (__position - cbegin());
// __x 可以是当前容器的某个元素,所以这里不直接移动__x,而是移动__x的副本
_Temporary_value __x_copy(this, __x);
_M_insert_aux(__pos, std::move(__x_copy._M_val()));
#else
_M_insert_aux(__position, __x);
#endif
}
// 容器已满,调用_M_realloc_insert重新申请内存并插入元素
else
#if __cplusplus >= 201103L
_M_realloc_insert(begin() + (__position - cbegin()), __x);
#else
_M_realloc_insert(__position, __x);
#endif
// 返回指向插入元素的迭代器
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)
#else
template<typename _Tp, typename _Alloc>
void vector<_Tp, _Alloc>::
_M_insert_aux(iterator __position, const _Tp& __x)
#endif
{
_GLIBCXX_ASAN_ANNOTATE_GROW(1);
// 先拷贝构造最后一个元素,插入容器尾端,同时更新_M_finish指针
_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
_Tp __x_copy = __x;
#endif
// [__position, _M_finish - 2)的所有元素往后挪一位,即移动到[__position + 1, _M_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
*__position = std::forward<_Arg>(__arg);
#endif
}
_M_insert_aux操作步骤示意图如下:
2)iterator insert(const_iterator __position, value_type&& __x); 在指定的__position位置插入元素__x,__x为右值引用。该函数操作类同上面的常量左值引用参数的函数版本。
#if __cplusplus >= 201103L
_GLIBCXX20_CONSTEXPR
iterator insert(const_iterator __position, value_type&& __x)
{ return _M_insert_rval(__position, std::move(__x)); }
#endif
#if __cplusplus >= 201103L
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);
}
// 插入位置不在容器末端,调用_M_insert_aux在指定位置插入元素
else
_M_insert_aux(begin() + __n, std::move(__v));
// 容器已满,调用_M_realloc_insert重新申请内存并插入元素
else
_M_realloc_insert(begin() + __n, std::move(__v));
// 返回指向插入元素的迭代器
return iterator(this->_M_impl._M_start + __n);
}
#endif
3)iterator insert(const_iterator __position, initializer_list<value_type> __l);在指定位置,插入初始化列表。
#if __cplusplus >= 201103L
iterator insert(const_iterator __position, initializer_list<value_type> __l)
{
auto __offset = __position - cbegin();
_M_range_insert(begin() + __offset, __l.begin(), __l.end(), std::random_access_iterator_tag());
return begin() + __offset;
}
#endif
调用的方式形如:
insert(begin(), {1,2,3});
4)iterator insert(const_iterator __position, size_type __n, const value_type& __x);在指定位置,插入__n个值为__x的数据。
#if __cplusplus >= 201103L
_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;
}
#else
void insert(iterator __position, size_type __n, const value_type& __x)
{ _M_fill_insert(__position, __n, __x); }
#endif
C++98和C++11及以上版本,该函数的返回值不同,C++98返回void,C++11返回指向插入的第一个元素的迭代器。
其中子接口_M_fill_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)
{
#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);
// __position到容器末端end,剩余元素个数__elems_after大于待插入的元素个数__n
if (__elems_after > __n)
{
_GLIBCXX_ASAN_ANNOTATE_GROW(__n);
// 先将容器最后__n个元素移动到容器末端,中间腾出__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());
// 更新_M_finish指针
this->_M_impl._M_finish += __n;
_GLIBCXX_ASAN_ANNOTATE_GREW(__n);
// 移动或拷贝[__position, __old_finish - __n)到[__old_finish - (__elems_after - __n), __old_finish)
_GLIBCXX_MOVE_BACKWARD3(__position.base(), __old_finish - __n, __old_finish);
// 以__x_copy填充[__position, __position + __n)
std::fill(__position.base(), __position.base() + __n, __x_copy);
}
// __position到容器末端end,剩余元素个数__elems_after小于等于待插入的元素个数__n
else
{
_GLIBCXX_ASAN_ANNOTATE_GROW(__n);
// 先填充__n - __elems_after个数据到容器末端
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);
// 移动[__position, __old_finish)到容器末端
std::__uninitialized_move_a(__position.base(), __old_finish,
this->_M_impl._M_finish, _M_get_Tp_allocator());
// 更新_M_finish指针
this->_M_impl._M_finish += __elems_after;
_GLIBCXX_ASAN_ANNOTATE_GREW(__elems_after);
// 填充[__position,__old_finish)
std::fill(__position.base(), __old_finish, __x_copy);
}
}
// 容器剩余空间不足
else
{
// 如果size() > __n,则容器新申请的空间大小为2倍size(), 否则为size()+__n
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
{
// 先在新的容器空间上填充[__position, __position + __n)
std::__uninitialized_fill_n_a(__new_start + __elems_before,
__n, __x, _M_get_Tp_allocator());
__new_finish = pointer();
// 移动[_M_start, __position)区间的数据到新的内存空间上
__new_finish = std::__uninitialized_move_if_noexcept_a
(this->_M_impl._M_start, __position.base(), __new_start, _M_get_Tp_allocator());
__new_finish += __n;
// 移动[__position, _M_finish)区间的数据到新的内存空间上
__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;
}
}
}
// 类型为bool的特化版本
template<typename _Alloc>
_GLIBCXX20_CONSTEXPR
void vector<bool, _Alloc>::
_M_fill_insert(iterator __position, size_type __n, bool __x)
{
if (__n == 0)
return;
// 剩余空间充足
if (capacity() - size() >= __n)
{
std::copy_backward(__position, end(), this->_M_impl._M_finish + difference_type(__n));
std::fill(__position, __position + difference_type(__n), __x);
this->_M_impl._M_finish += difference_type(__n);
}
// 剩余空间不足
else
{
const size_type __len = _M_check_len(__n, "vector<bool>::_M_fill_insert");
_Bit_pointer __q = this->_M_allocate(__len);
iterator __start(std::__addressof(*__q), 0);
iterator __i = _M_copy_aligned(begin(), __position, __start);
std::fill(__i, __i + difference_type(__n), __x);
iterator __finish = std::copy(__position, end(), __i + difference_type(__n));
this->_M_deallocate();
this->_M_impl._M_end_of_storage = __q + _S_nword(__len);
this->_M_impl._M_start = __start;
this->_M_impl._M_finish = __finish;
}
}
_M_fill_insert(iterator __position, size_type __n, const value_type& __x)用于在__position位置插入__n个值为__x的对象。当容器剩余空间充足时,插入步骤类同_M_range_insert;当容器剩余空间不足时,插入步骤类同_M_realloc_insert
5)iterator insert(const_iterator __position, _InputIterator __first, _InputIterator __last);在指定位置,插入[__first, __last)区间的数据。
#if __cplusplus >= 201103L
template<typename _InputIterator, typename = std::_RequireInputIter<_InputIterator>>
_GLIBCXX20_CONSTEXPR
iterator insert(const_iterator __position, _InputIterator __first, _InputIterator __last)
{
difference_type __offset = __position - cbegin();
_M_insert_dispatch(begin() + __offset, __first, __last, __false_type());
// 返回指向插入位置的迭代器
return begin() + __offset;
}
#else
template<typename _InputIterator>
void insert(iterator __position, _InputIterator __first, _InputIterator __last)
{
// 检查_InputIterator类型是否为整型,非整型时,为迭代器
typedef typename std::__is_integer<_InputIterator>::__type _Integral;
_M_insert_dispatch(__position, __first, __last, _Integral());
}
#endif
// 模板参数为整型数据时调用
template<typename _Integer>
_GLIBCXX20_CONSTEXPR
void
_M_insert_dispatch(iterator __pos, _Integer __n, _Integer __val, __true_type)
{ _M_fill_insert(__pos, __n, __val); }
// 模板参数为迭代器时调用
template<typename _InputIterator>
_GLIBCXX20_CONSTEXPR
void _M_insert_dispatch(iterator __pos, _InputIterator __first, _InputIterator __last, __false_type)
{
_M_range_insert(__pos, __first, __last, std::__iterator_category(__first));
}
C++98和C++11及以上版本,该函数的返回值不同,C++98返回void,C++11返回指向插入的第一个元素的迭代器。
6. vector的重载运算符
6.1 移动赋值运算符重载和初始化列表赋值
#if __cplusplus >= 201103L
// 移动赋值运算符重载
_GLIBCXX20_CONSTEXPR vector& operator=(vector&& __x) noexcept(_Alloc_traits::_S_nothrow_move())
{
constexpr bool __move_storage = _Alloc_traits::_S_propagate_on_move_assign()
|| _Alloc_traits::_S_always_equal();
_M_move_assign(std::move(__x), __bool_constant<__move_storage>());
return *this;
}
// 赋值运算符重载,支持初始化列表的赋值方式,如vector vec = {1, 2, 3};
_GLIBCXX20_CONSTEXPR vector& operator=(initializer_list<value_type> __l)
{
this->_M_assign_aux(__l.begin(), __l.end(), random_access_iterator_tag());
return *this;
}
// 分配器支持移动赋值
_GLIBCXX20_CONSTEXPR
void _M_move_assign(vector&& __x, true_type) noexcept
{
// 交换两个容器指针
vector __tmp(get_allocator());
this->_M_impl._M_swap_data(__x._M_impl);
__tmp._M_impl._M_swap_data(__x._M_impl);
std::__alloc_on_move(_M_get_Tp_allocator(), __x._M_get_Tp_allocator());
}
_GLIBCXX20_CONSTEXPR
void _M_move_assign(vector&& __x, false_type)
{
// 分配器支持移动赋值
if (__x._M_get_Tp_allocator() == this->_M_get_Tp_allocator())
_M_move_assign(std::move(__x), true_type());
else
{
// 分配器不支持移动赋值,则调用_M_assign_aux赋值新容器,直接移动容器内的每个元素
this->_M_assign_aux(std::make_move_iterator(__x.begin()),
std::make_move_iterator(__x.end()), std::random_access_iterator_tag());
__x.clear();
}
}
#endif
6.2 赋值运算符重载
template<typename _Tp, typename _Alloc>
_GLIBCXX20_CONSTEXPR
vector<_Tp, _Alloc>& vector<_Tp, _Alloc>::
operator=(const vector<_Tp, _Alloc>& __x)
{
if (std::__addressof(__x) != this)
{
_GLIBCXX_ASAN_ANNOTATE_REINIT;
#if __cplusplus >= 201103L
if (_Alloc_traits::_S_propagate_on_copy_assign())
{
// 分配器不支持移动赋值,即旧分配器申请的内存不能由新分配器来释放,
// 这种情况需要析构原容器内的数据对象,并释放内存空间。
if (!_Alloc_traits::_S_always_equal()
&& _M_get_Tp_allocator() != __x._M_get_Tp_allocator())
{
// replacement allocator cannot free existing storage
this->clear();
_M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage
- this->_M_impl._M_start);
this->_M_impl._M_start = nullptr;
this->_M_impl._M_finish = nullptr;
this->_M_impl._M_end_of_storage = nullptr;
}
std::__alloc_on_copy(_M_get_Tp_allocator(), __x._M_get_Tp_allocator());
}
#endif
const size_type __xlen = __x.size();
// 新容器容量大于原容器容量,重新申请内存并拷贝新容器数据,析构原容器对象,并释放原容器内存空间。
if (__xlen > capacity())
{
pointer __tmp = _M_allocate_and_copy(__xlen, __x.begin(), __x.end());
std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish, _M_get_Tp_allocator());
_M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage - this->_M_impl._M_start);
this->_M_impl._M_start = __tmp;
this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __xlen;
}
// 原容器数据个数大于等于新容器数据个数,将新容器的数据拷贝至原容器首部,并析构尾部多余的旧数据对象
else if (size() >= __xlen)
{
std::_Destroy(std::copy(__x.begin(), __x.end(), begin()), end(), _M_get_Tp_allocator());
}
// 原容器数据个数小于新容器数据个数,先拷贝size()个元素到新容器,再拷贝构造剩余的数据。
else
{
std::copy(__x._M_impl._M_start, __x._M_impl._M_start + size(), this->_M_impl._M_start);
std::__uninitialized_copy_a(__x._M_impl._M_start + size(),
__x._M_impl._M_finish, this->_M_impl._M_finish, _M_get_Tp_allocator());
}
this->_M_impl._M_finish = this->_M_impl._M_start + __xlen;
}
return *this;
}
6.3 []运算符重载
// []运算符重载,使vector支持随机存取,返回容器第__n个对象的引用
_GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR
reference operator[](size_type __n) _GLIBCXX_NOEXCEPT
{
__glibcxx_requires_subscript(__n);
return *(this->_M_impl._M_start + __n);
}
_GLIBCXX_NODISCARD _GLIBCXX20_CONSTEXPR
const_reference operator[](size_type __n) const _GLIBCXX_NOEXCEPT
{
__glibcxx_requires_subscript(__n);
return *(this->_M_impl._M_start + __n);
}
本文来自博客园,作者:流翎,转载请注明原文链接:https://www.cnblogs.com/hjx168/p/16201115.html
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)