前言
最近事情都完成差不多了,看看vecotr实现吧。vector算是stl中很常见的容器了,可能很多人学完c++语法,接触stl就是从vector开始的。vector的数据结构就不细细说明了,相信大家对数组这数据结构绝对都懂。
另外看了标准库才明白要达到Standard Template实际上是很复杂的一件事。一些非常见的函数就只看看声明吧,以及一些类型萃取就不展示了,要完整的展示出来实在太太长了。一份vector<T>加上vector<bool>的源码大概在4000行左右。
STL版本
本文所使用的stl版本为libc++ 13.0,属于LLVM项目。
如果遇到不熟悉的宏定义可以参考文档Symbol Visibility Macros
__vector_base_common
在介绍__vector_base_common之前要明确下vector是一个子类。它的继承关系为
__vector_base_common <-- __vector_base <-- vector
__vector_base_common为父类,派生出__vector_base,再派生出vector,其中利用CRTP的编程方法,这个我们后面在说
我们先看看声明
template <bool>
class _LIBCPP_TEMPLATE_VIS __vector_base_common
{
protected:
_LIBCPP_INLINE_VISIBILITY __vector_base_common() {
}
_LIBCPP_NORETURN void __throw_length_error() const;
_LIBCPP_NORETURN void __throw_out_of_range() const;
};
整个__vector_base_common较为简单,内部除了构造函数外,剩下函数都为抛出错误的函数。
在声明稍后一点的地方会一个比较奇怪的代码。
_LIBCPP_EXTERN_TEMPLATE(class _LIBCPP_EXTERN_TEMPLATE_TYPE_VIS __vector_base_common<true>)
我将_LIBCPP_EXTERN_TEMPLATE这个宏定义找出来。
// Libc++ allows disabling extern template instantiation declarations by
// means of users defining _LIBCPP_DISABLE_EXTERN_TEMPLATE.
//
// Furthermore, when the Debug mode is enabled, we disable extern declarations
// when building user code because we don't want to use the functions compiled
// in the library, which might not have had the debug mode enabled when built.
// However, some extern declarations need to be used, because code correctness
// depends on it (several instances in <locale>). Those special declarations
// are declared with _LIBCPP_EXTERN_TEMPLATE_EVEN_IN_DEBUG_MODE, which is enabled
// even when the debug mode is enabled.
#if defined(_LIBCPP_DISABLE_EXTERN_TEMPLATE)
# define _LIBCPP_EXTERN_TEMPLATE(...) /* nothing */
# define _LIBCPP_EXTERN_TEMPLATE_EVEN_IN_DEBUG_MODE(...) /* nothing */
#elif _LIBCPP_DEBUG_LEVEL >= 1 && !defined(_LIBCPP_BUILDING_LIBRARY)
# define _LIBCPP_EXTERN_TEMPLATE(...) /* nothing */
# define _LIBCPP_EXTERN_TEMPLATE_EVEN_IN_DEBUG_MODE(...) extern template __VA_ARGS__;
#else
# define _LIBCPP_EXTERN_TEMPLATE(...) extern template __VA_ARGS__;
# define _LIBCPP_EXTERN_TEMPLATE_EVEN_IN_DEBUG_MODE(...) extern template __VA_ARGS__;
#endif
简单的来说,在非debug模式,这个宏将被定义为19行的可变参宏。也就是extern template(…)这样的,对模板进行强制的实例化。
关于这样设计我推测可以有效的减少模板带来的代码膨胀,但是又对debug时候不太友好,所才定制了这种策略。
__vector_base
首先我们来看一下声明与内部别名
template <class _Tp, class _Allocator>
class __vector_base
: protected __vector_base_common<true>{
public:
typedef _Allocator allocator_type;
typedef allocator_traits<allocator_type> __alloc_traits;
typedef typename __alloc_traits::size_type size_type;
protected:
typedef _Tp value_type;
typedef value_type& reference;
typedef const value_type& const_reference;
typedef typename __alloc_traits::difference_type difference_type;
typedef typename __alloc_traits::pointer pointer;
typedef typename __alloc_traits::const_pointer const_pointer;
typedef pointer iterator;
typedef const_pointer const_iterator;
}
__vector_base是__vector_base_common<true>的子类,两个模板参数一个为内部数据类型与分配器。内部有大量的typedef。
接着我们来看一下__vector_base的成员,__vector_base只有三个。
pointer __begin_;
pointer __end_;
__compressed_pair<pointer, allocator_type> __end_cap_;
__begin_为首地址指针,__end_为数组结束位置指针,size()用此计算大小,\而__end_cap_包含数组最大空间的指针和空间分配器,capacity()函数用此计算大小。这就是vector的核心了成员。
接着整理一下__vector_base中出现我认为比较重要的函数吧,顺带一提其中_LIBCPP_INLINE_VISIBILITY具体含有我找了下StackOverflow。
本意是函数从abi隐藏,在以前是通过inline进行隐藏的,但是现在使用clang的attribute((internal_linkage))进行隐藏。
capacity函数实现
_LIBCPP_INLINE_VISIBILITY
void clear() _NOEXCEPT {
__destruct_at_end(__begin_);};
_LIBCPP_INLINE_VISIBILITY
size_type capacity() const _NOEXCEPT //计算空间大小的实现
{
return static_cast<size_type>(__end_cap() - __begin_);}
copy和move空间分配器
复制或者移动空间分配器类型。
_LIBCPP_INLINE_VISIBILITY
void __copy_assign_alloc(const __vector_base& __c, true_type)
{
if (__alloc() != __c.__alloc())
{
clear(); \\析构自己内部数据
__alloc_traits::deallocate(__alloc(), __begin_, capacity()); \\释放自己的空间
__begin_ = __end_ = __end_cap() = nullptr;
}
__alloc() = __c.__alloc();
}
_LIBCPP_INLINE_VISIBILITY
void __move_assign_alloc(__vector_base& __c, true_type)
_NOEXCEPT_(is_nothrow_move_assignable<allocator_type>::value)
{
__alloc() = _VSTD::move(__c.__alloc());
}
__alloc()函数作用是返回相应类型的空间分配器
内容析构
template <class _Tp, class _Allocator>
inline _LIBCPP_INLINE_VISIBILITY
void
__vector_base<_Tp, _Allocator>::__destruct_at_end(pointer __new_last) _NOEXCEPT
{
pointer __soon_to_be_end = __end_;
while (__new_last != __soon_to_be_end)
__alloc_traits::destroy(__alloc(), _VSTD::__to_address(--__soon_to_be_end)); //析构
__end_ = __new_last;
}
基本上__vector_base就没有什么东西,剩下的都在vector中实现了。
vector
终于到vector的本体了,老规矩,先看声明
template <class _Tp, class _Alloc = allocator<_Tp> > //声明
class _LIBCPP_TEMPLATE_VIS vector;
template <class _Tp, class _Allocator /* = allocator<_Tp> */> //定义
class _LIBCPP_TEMPLATE_VIS vector
: private __vector_base<_Tp, _Allocator> //利用CRTP
{
private:
typedef __vector_base<_Tp, _Allocator> __base;
typedef allocator<_Tp> __default_allocator_type;
public:
typedef vector __self;
typedef _Tp value_type;
typedef _Allocator allocator_type;
typedef typename __base::__alloc_traits __alloc_traits;
typedef typename __base::reference reference;
typedef typename __base::const_reference const_reference;
typedef typename __base::size_type size_type;
typedef typename __base::difference_type difference_type;
typedef typename __base::pointer pointer;
typedef typename __base::const_pointer const_pointer;
typedef __wrap_iter<pointer> iterator;
typedef __wrap_iter<const_pointer> const_iterator;
typedef _VSTD::reverse_iterator<iterator> reverse_iterator;
typedef _VSTD::reverse_iterator<const_iterator> const_reverse_iterator;
static_assert((is_same<typename allocator_type::value_type, value_type>::value), //c++11 编译期断言
"Allocator::value_type must be same type as value_type");
}
//顺带一提,这个声明在iosfwd文件中,第一次看的时候没有找到,看的是定义,还纳闷第二个模板参数的默认值是什么。
顺便也想解释下一些vector为什么要套两层的继承,第一次继承,__vector_base_common<true>被声明为extern template减少代码的膨胀,第二次继承利用CRPT,实现编译期的多态。
构造函数
vector构造函数最简单的就是支持InputIterator和_ForwardIterator类型作为参数。至于其他如initializer_list等为参数类型的构造函数就不一一列出了。这里以InputIterator版本为例。
template <class _InputIterator>
vector(_InputIterator __first,
typename enable_if<__is_cpp17_input_iterator <_InputIterator>::value &&
!__is_cpp17_forward_iterator<_InputIterator>::value &&
is_constructible<
value_type,
typename iterator_traits<_InputIterator>::reference>::value,
_InputIterator>::type __last);
如果刚入门c++看这个必然头大,不过我们分解一些就很好理解了。
首先要认清这个构造函数只有两个参数,只不过第二个参数的类型长了一点,是因为这里做了个类型检查。我们重点分解析第二个参数。
typename enable_if<__is_cpp17_input_iterator <_InputIterator>::value &&
!__is_cpp17_forward_iterator<_InputIterator>::value &&
is_constructible<
value_type,
typename iterator_traits<_InputIterator>::reference>::value,
_InputIterator>::type __last
第二个参数名称为__last,实际上也是个_InputIterator类型。首先先enable_if,enable_if有两个模板参数,第一个为bool值,第二个为类型,只有当第一个模板参数为true时候,enable_if<>::type为第二个参数的类型。最外面enable_if是一个类型检查,如果类型不对,则编译报错,接下来我们看enable_if的第一个模板参数
__is_cpp17_input_iterator <_InputIterator>::value && //判断是否是input_iterator
!__is_cpp17_forward_iterator<_InputIterator>::value && //判断是否是forward_iterator
is_constructible< //利用类型萃取,判断是否存在class A(A &parm)拷贝构造函数
value_type,
typename iterator_traits<_InputIterator>::reference>::value
另一个构造函数也是差不多如此,就不细说了。
template <class _InputIterator>
vector(_InputIterator __first, _InputIterator __last, const allocator_type& __a,
typename enable_if<__is_cpp17_input_iterator <_InputIterator>::value &&
!__is_cpp17_forward_iterator<_InputIterator>::value &&
is_constructible<
value_type,
typename iterator_traits<_InputIterator>::reference>::value>::type* = 0);
插入函数
插入函数就两个push_back和emplace_back。先看下emplace_back吧
emplace_back
//if _LIBCPP_STD_VER > 14,实际上这里是用宏控制c++11和c++14两个版本,14和17区别就是是否返回自身的引用
template <class _Tp, class _Allocator>
template <class... _Args>
inline
#if _LIBCPP_STD_VER > 14
typename vector<_Tp, _Allocator>::reference
#else
void
#endif
vector<_Tp, _Allocator>::emplace_back(_Args&&... __args)
{
if (this->__end_ < this->__end_cap())
{
__construct_one_at_end(_VSTD::forward<_Args>(__args)...);
}
else
__emplace_back_slow_path(_VSTD::forward<_Args>(__args)...);
#if _LIBCPP_STD_VER > 14
return this->back();
#endif
}
可以看到emplace_back的参数是一个折叠的右值引用,这里用右值引用是利用了折叠引用的特性,然后再使用foward进行参数转发。
在内部空间足够存储的情况下调用__construct_one_at_end()函数,如果不足则调用__emplace_back_slow_path;
首先我们来看下__construct_one_at_end()函数,这个函数在可用空间原地构造。
template <class ..._Args>
_LIBCPP_INLINE_VISIBILITY
void __construct_one_at_end(_Args&& ...__args) {
_ConstructTransaction __tx(*this, 1); //简单的来说这个结构体是用来移动__end指针的,参数1就是向后移动1个距离。
__alloc_traits::construct(this->__alloc(), _VSTD::__to_address(__tx.__pos_),
_VSTD::forward<_Args>(__args)...);
++__tx.__pos_;
}
};
其中定义了一个_ConstructTransaction结构体,用来操作vector内部的指针。然后使用__alloc_traits进行placement_new。参数…__args为构造函数的参数,特别注意下__args利用了可变参模板和折叠表达式来匹配构造函数的参数。
接着看空间不足时候的__emplace_back_slow_path()函数
template <class _Tp, class _Allocator>
template <class... _Args>
void
vector<_Tp, _Allocator>::__emplace_back_slow_path(_Args&&... __args)
{
allocator_type& __a = this->__alloc();
__split_buffer<value_type, allocator_type&> __v(__recommend(size() + 1), size(), __a);
\\__recommend(size() + 1)为最大cap,在未达到空间分配器上限时为乘以2,size()为现在要构造的位置,__a为分配器
// __v.emplace_back(_VSTD::forward<_Args>(__args)...);
__alloc_traits::construct(__a, _VSTD::__to_address(__v.__end_), _VSTD::forward<_Args>(__args)...); //在新的空间上构造
__v.__end_++;
__swap_out_circular_buffer(__v); //交换
}
这里就是比较经典两倍扩容了,__split_buffer构造出两倍的空间,接着__alloc_traits::construct构造,最后使用__swap_out_circular_buffer交换回来。关于__split_buffer,我推测是一个多种容器通用的split_buffer,用来解决容器扩容,内部有对多种迭代器以及指针的构造函数。
push_back
push_back其实和emplace_back基本差不多,只不过是制作了左值和右值两个版本。
//左值版本
template <class _Tp, class _Allocator>
inline _LIBCPP_INLINE_VISIBILITY
void
vector<_Tp, _Allocator>::push_back(const_reference __x)
{
if (this->__end_ != this->__end_cap())
{
__construct_one_at_end(__x);
}
else
__push_back_slow_path(__x);
}
//右值版本
template <class _Tp, class _Allocator>
inline _LIBCPP_INLINE_VISIBILITY
void
vector<_Tp, _Allocator>::push_back(value_type&& __x)
{
if (this->__end_ < this->__end_cap())
{
__construct_one_at_end(_VSTD::move(__x));
}
else
__push_back_slow_path(_VSTD::move(__x));
}
用到的两个辅助函数和emplace_back一样。