STL源码剖析之Allocator&vector
结合以前学习《STL源码剖析》笔记、理解(与当前版本部分实现细节存在差异),针对gcc6.4.1 STL源码(基于cpp11特性)的设计思想、实现细节作了分析。列出了一系列重要函数,对部分重点设计方式、实现细节做了注释。由于个人技术及文档编排水平有限,可能存在各种问题,欢迎指出及交流讨论。后续会基于本人的优化经验,发布出基于STL底层实现的优化。当然也会分享一些使用SGI STL的注意事项。本系列会保持更新。
文章目录
对于STL标准要求的std::allocator
, SGI STL并没有严格按照此标准,而是设计实现了一个专属的空间配置器,即std::alloc
。当然SGI STL对于std::allocator
也提供了一个标准分配器接口,即simple_alloc
,但其只是对new
和 delete
的简单封装。
SGI STL配置器定义于<memory>
内,其中包含以下文件:
1)stl_construct.h
:定义了全局函数construc()
和destroy()
,负责对象的构造和析构。SGI对于容器的大规模元素初值的设置设计了优化,最佳情况下会调用memmove(),最差情况下会调用construct()
2)stl_alloc.h
:定义了一、二级配置器。
3)stl_uninitialized.h
:定义了一系列全局函数,用来填充(fill)、复制(copy)大块内存数据,如:un_initialized_copy()
、un_initialized_fill()
、un_initialized_fill_n()
1.对象构造/析构 construct()&destroy()
SGI STL对对象的构造和析构定义于<stl_construct.h>
内,部分内容如下:
// 非标准构造/析构函数
/** @file bits/stl_construct.h
*/
#ifndef _STL_CONSTRUCT_H
#define _STL_CONSTRUCT_H 1
#include <new>
#include <bits/move.h>
#include <ext/alloc_traits.h>
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
/**
* 使用空间配置器分配内存后构造对象
*/
//使用可变参模板 调用::new()
#if __cplusplus >= 201103L
template<typename _T1, typename... _Args>
inline void
_Construct(_T1* __p, _Args&&... __args)
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
#else
template<typename _T1, typename _T2>
inline void
_Construct(_T1* __p, const _T2& __value)
{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 402. wrong new expression in [some_]allocator::construct
::new(static_cast<void*>(__p)) _T1(__value);
}
#endif
/**
* Destroy the object pointed to by a pointer type.
*/
//析构所指对象,接受指向对象的指针
template<typename _Tp>
inline void
_Destroy(_Tp* __pointer)
{ __pointer->~_Tp(); }
//辅助析构函数,接受两个迭代器
template<bool>
struct _Destroy_aux
{
template<typename _ForwardIterator>
static void
__destroy(_ForwardIterator __first, _ForwardIterator __last)
{
for (; __first != __last; ++__first)
std::_Destroy(std::__addressof(*__first));
}
};
//对于_Destroy_aux类的特化,辅助析构函数
template<>
struct _Destroy_aux<true>
{
template<typename _ForwardIterator>
static void
__destroy(_ForwardIterator, _ForwardIterator) { }
};
/*析构一系列区间内的对象,如果对象的value_type含有trivial destructor,
* 会采用优化的方式析构,否则会调用该对象自身的析构函数
* type_traits 提供了编译期计算、判断、转换、查询等,提供了编译期的true和false*/
/*根据上述_Destroy_aux类调用__destroy()*/
template<typename _ForwardIterator>
inline void
_Destroy(_ForwardIterator __first, _ForwardIterator __last)
{
typedef typename iterator_traits<_ForwardIterator>::value_type
_Value_type;
std::_Destroy_aux<__has_trivial_destructor(_Value_type)>::
__destroy(__first, __last);
}
/*根据提供的配置器析构区间内一系列对象,同样地,对于非默认配置器无法进行优化,
* 即使_Tp包含一个trivial destructor
* */
//若采用非默认配置器,无法优化,需要使用特备重载的destroy()
template<typename _ForwardIterator, typename _Allocator>
void
_Destroy(_ForwardIterator __first, _ForwardIterator __last,
_Allocator& __alloc)
{
typedef __gnu_cxx::__alloc_traits<_Allocator> __traits;
for (; __first != __last; ++__first)
__traits::destroy(__alloc, std::__addressof(*__first));
}
//若使用默认配置器,可以优化,使用_Destroy,类型萃取判断是否含有trivial类型dtor
template<typename _ForwardIterator, typename _Tp>
inline void
_Destroy(_ForwardIterator __first, _ForwardIterator __last,
allocator<_Tp>&)
{
_Destroy(__first, __last);
}
} // namespace std
#endif /* _STL_CONSTRUCT_H */
/*
__type_traits负责萃取型别特性,通常我们关注的型别特性有是否具备non-trivial defalt ctor?
是否具备non-trivial copy ctor?是否具备non-trivial assignment operator?
是否具备non-trivial dtor?如果答案是否定的(也就是说具备这些特性)就对这个型别进行构造,
析构, 拷贝, 赋值等操作,这样可以采用最有效率的措施。(比如一些自己定义的类,或者不是普通类型比如string类,
对象等,就要进行这样的操作)如果答案是确定的(也就是说不具备这些特性,例如int, float. double, char等普通的类型就不具备这种特性)
这些类型为了提高效率直接采用内存处理操作,例如malloc() memcpy(),所以这种机制对于大规模操作频繁的容器有着显著的效率的提升。
*/
2.空间的配置与释放Allocator
SGI STL对于空间的配置和释放设计采用了“两层”的方式。cpp基本的内存配置和释放的操作为::operator new()
和 ::operator delete()
。
过多的“小型区块”会造成的内存碎片问题,并且配置时额外的负担也是一个大问题,额外的负担永远无法避免,毕竟系统要靠这多出来的空间来管理内存,但是区块越小,额外负担所占的比例越大,就越浪费。 所以对小型区块采取了特殊的处理。
“两层”式的配置器设计了分配/释放内存阈值max_bytes,分配的内存size>_S_max_bytes,使用operator new()/operator delete()直接分配/释放,若size<_S_max_bytes,使用内存池分配/释放内存
当前版本的SGI STL默认使用std::allocator()
,除此以外,在c++扩展(/ext)中支持了其他几中配置器:
new_allocator
对于::operator new 和 ::operator delete的简单封装
malloc_allocator
对于malloc和free的简单封装,也是out-of-memory的处理入口。
debug_allocator
任意分配器A的包装器。它将稍微增加的大小请求传递给A,并使用额外的内存来存储大小信息。将指针传递给deallocate()时,将检查存储的大小,并使用assert()来确保它们匹配。
throw_allocator
包括内存跟踪和标记功能以及以可配置的间隔抛出异常的钩子
pool_alloc
一种高性能的基于内存池的分配器。可重用内存在该类型的相同实例化之间共享。当列表用完时,它调用::operator new()
以获得新内存。如果客户机容器请求的块大于某个阈值大小,则绕过池,并将allocate/deallocate请求直接传递给::operator new
。
这个类的旧版本采用一个名为thr的布尔模板参数和一个名为inst的整数模板参数。
__mt_alloc
一种高性能的固定大小的分配程序,分配呈指数级增加。
bitmap_allocator
一种高性能分配器,它使用位图来跟踪已用和未使用的内存位置。.
pool_allocator.h
#ifndef _POOL_ALLOCATOR_H
#define _POOL_ALLOCATOR_H 1
#include <bits/c++config.h>
#include <cstdlib>
#include <new>
#include <bits/functexcept.h>
#include <ext/atomicity.h>
#include <ext/concurrence.h>
#include <bits/move.h>
#if __cplusplus >= 201103L
#include <type_traits>
#endif
namespace __gnu_cxx _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION
using std::size_t;
using std::ptrdiff_t;
/*
* __pool_alloc的基类
* 使用各种分配器来完成底层请求(在默认的高速内存池模式下尽可能少地发出请求)
*
* 若要求全局管理,则使用::new()
* 若请求的对象size>_S_max_bytes, 得到的对象直接由new()获取
* 其他情况,使用_S_round_up()函数对size上调至8的倍数,
* 因此,客户端有足够的大小信息,我们可以将对象返回到适当的空闲列表中,而不会永久丢失部分对象。
*
* */
class __pool_alloc_base
{
protected:
//上调边界(最小区块)
enum { _S_align = 8 };
//分配内存方式的阈值,分配的内存size>_S_max_bytes,使用malloc直接分配,若size<_S_max_bytes,使用内存池分配
enum { _S_max_bytes = 128 };
//freelist 数目
enum { _S_free_list_size = (size_t)_S_max_bytes / (size_t)_S_align };
//freelist节点构造,使用union节省内存
union _Obj
{
union _Obj* _M_free_list_link;
char _M_client_data[1];
};
//freelist链表数组
static _Obj* volatile _S_free_list[_S_free_list_size];
// Chunk allocation state.
static char* _S_start_free; //内存池起始位置
static char* _S_end_free; //内存池结束位置
static size_t _S_heap_size; //内存池
//上调至8的倍数
//byte+7 再与11111000按位与
size_t
_M_round_up(size_t __bytes)
{ return ((__bytes + (size_t)_S_align - 1) & ~((size_t)_S_align - 1)); }
_GLIBCXX_CONST _Obj* volatile*
_M_get_free_list(size_t __bytes) throw ();
__mutex&
_M_get_mutex() throw ();
// Returns an object of size __n, and optionally adds to size __n
// 返回一个大小为n的对象,并可能加入大小为n的其他区块到free_list
void*
_M_refill(size_t __n);
/*配置一大块空间,可容纳nobjs个大小为size的区块,如果无法配置nobjs个区块,nobjs会降低*/
char*
_M_allocate_chunk(size_t __n, int& __nobjs);
};
//使用内存池的配置器
//维护了一个链表数组,每个链表的节点都是不同大小的内存块 mem_block,大小分别是8、16、24 ... 128byte。
//每次要分配内存的时候,就选择最适合的块大小来分配。如### 1.对象构造/析构 construct()&destroy()
### 2.空间的配置与释放 Allocator
### 3.内存的基本处理工具果没有找到对应的freelist,就重新填充(refill())freelist。
//如果遇到了块不够的情况,就从内存池中分配内存,然后切成block,并加入到对应链表中。
template<typename _Tp>
class __pool_alloc : private __pool_alloc_base
{
private:
static _Atomic_word _S_force_new;
public:
typedef size_t size_type;
typedef ptrdiff_t difference_type;
typedef _Tp* pointer;
typedef const _Tp* const_pointer;
typedef _Tp& reference;
typedef const _Tp& const_reference;
typedef _Tp value_type;
//rebind使用相同的内存分配策略,对不同类型特化
//__pool_alloc<_Tp>::rebind<_Tp1>::other 即__pool_alloc<_Tp1>
template<typename _Tp1>
struct rebind
{ typedef __pool_alloc<_Tp1> other; };
#if __cplusplus >= 201103L
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2103. propagate_on_container_move_assignment
typedef std::true_type propagate_on_container_move_assignment;
#endif
__pool_alloc() _GLIBCXX_USE_NOEXCEPT { }
__pool_alloc(const __pool_alloc&) _GLIBCXX_USE_NOEXCEPT { }
template<typename _Tp1>
__pool_alloc(const __pool_alloc<_Tp1>&) _GLIBCXX_USE_NOEXCEPT { }
~__pool_alloc() _GLIBCXX_USE_NOEXCEPT { }
pointer
address(reference __x) const _GLIBCXX_NOEXCEPT
{ return std::__addressof(__x); }
const_pointer
address(const_reference __x) const _GLIBCXX_NOEXCEPT
{ return std::__addressof(__x); }
size_type
max_size() const _GLIBCXX_USE_NOEXCEPT
{ return size_t(-1) / sizeof(_Tp); }
#if __cplusplus >= 201103L
template<typename _Up, typename... _Args>
void
construct(_Up* __p, _Args&&... __args)
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
template<typename _Up>
void
destroy(_Up* __p) { __p->~_Up(); }
#else
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 402. wrong new expression in [some_] allocator::construct
void
construct(pointer __p, const _Tp& __val)
{ ::new((void *)__p) _Tp(__val); }
void
destroy(pointer __p) { __p->~_Tp(); }
#endif
pointer
allocate(size_type __n, const void* = 0);
void
deallocate(pointer __p, size_type __n);
};
template<typename _Tp>
inline bool
operator==(const __pool_alloc<_Tp>&, const __pool_alloc<_Tp>&)
{ return true; }
template<typename _Tp>
inline bool
operator!=(const __pool_alloc<_Tp>&, const __pool_alloc<_Tp>&)
{ return false; }
template<typename _Tp>
_Atomic_word
__pool_alloc<_Tp>::_S_force_new;
template<typename _Tp>
_Tp*
__pool_alloc<_Tp>::allocate(size_type __n, const void*)
{
pointer __ret = 0;
if (__builtin_expect(__n != 0, true))
{
if (__n > this->max_size())
std::__throw_bad_alloc();
/*如果此处有一个竞态关系,根据getenv()获取环境变量进行增减处理(基于basic_string对多线程的支持)*/
if (_S_force_new == 0)
{
if (std::getenv("GLIBCXX_FORCE_NEW"))
__atomic_add_dispatch(&_S_force_new, 1);
else
__atomic_add_dispatch(&_S_force_new, -1);
}
const size_t __bytes = __n * sizeof(_Tp);
//大块的内存采用::operator new() 分配,并由其处理异常
if (__bytes > size_t(_S_max_bytes) || _S_force_new > 0)
__ret = static_cast<_Tp*>(::operator new(__bytes));
else //小内存从freelist中取
{
_Obj* volatile* __free_list = _M_get_free_list(__bytes);
__scoped_lock sentry(_M_get_mutex());
//得到对应的freelist
//__restrict__: 修饰指针指向的内存不能被别的指针引用
_Obj* __restrict__ __result = *__free_list;
//若没找到对应的freelist,重新填充freelist
if (__builtin_expect(__result == 0, 0))
__ret = static_cast<_Tp*>(_M_refill(_M_round_up(__bytes)));
//如果找到了合适的链表
//调整free_list第一个链表节点的指向
//将my_free_list也就是该类型链表的第一个节点指向第二个节点
//因为这个链表上的第一块内存要被分配使用了
else
{
*__free_list = __result->_M_free_list_link;
__ret = reinterpret_cast<_Tp*>(__result);
}
//异常处理
if (__ret == 0)
std::__throw_bad_alloc();
}
}
return __ret;
}
template<typename _Tp>
void
__pool_alloc<_Tp>::deallocate(pointer __p, size_type __n)
{
if (__builtin_expect(__n != 0 && __p != 0, true))
{
const size_t __bytes = __n * sizeof(_Tp);
//若内存大于128时,使用::operator delete()
if (__bytes > static_cast<size_t>(_S_max_bytes) || _S_force_new > 0)
::operator delete(__p);
//若内存小于128
else
{
//寻找对应的freelist
_Obj* volatile* __free_list = _M_get_free_list(__bytes);
_Obj* __q = reinterpret_cast<_Obj*>(__p);
__scoped_lock sentry(_M_get_mutex());
//将要回收的区块的下个节点指向第一个节点
__q ->_M_free_list_link = *__free_list;
//调整free_list第一个链表节点的指向
*__free_list = __q;
}
}
}
_GLIBCXX_END_NAMESPACE_VERSION
} // namespace
#endif
3.内存的基本处理工具
作用于未初始化空间上的全局函数:construct()
、destroy()
以及uninitialized_copy()
、uninitialized_fill()
、uninitialized_fill_n()
,后三个系列函数定义于<stl_uninitialized>
中。并且是copy()
、fill()
等函数(位于stl_algobase.h
)的底层实现,
copy()
函数会尽可能的优化(在萃取得到型别的基础上),包括使用函数重载,型别特性,偏特化等方法,尽可能的使用memmove()
等底层内存操作,是性能提升的保障。
通过这几个函数,使我们能够将内存的配置与对象的构造行为分离开。对于未初始化的区域内根据萃取得到的trivial类型与否调用不同类型的copy constructor.
//uninitialized_copy() -->__uninit_copy() --> copy()
//uninitialized_copy()类似copy()函数,但不需要一个初始化的输出区间
template<typename _InputIterator, typename _ForwardIterator>
inline _ForwardIterator
uninitialized_copy(_InputIterator __first, _InputIterator __last,
_ForwardIterator __result)
{
typedef typename iterator_traits<_InputIterator>::value_type
_ValueType1;
typedef typename iterator_traits<_ForwardIterator>::value_type
_ValueType2;
#if __cplusplus < 201103L
const bool __assignable = true;
#else
// trivial types can have deleted assignment
typedef typename iterator_traits<_InputIterator>::reference _RefType1;
typedef typename iterator_traits<_ForwardIterator>::reference _RefType2;
const bool __assignable = is_assignable<_RefType2, _RefType1>::value;
#endif
//__uninit_copy
return std::__uninitialized_copy<__is_trivial(_ValueType1)
&& __is_trivial(_ValueType2)
&& __assignable>::
__uninit_copy(__first, __last, __result);
}
//__uninit_copy():使用型别萃取,对于trivial类型,调用copy()(可采用memcpy()/memmove()等,效率更高,),
//对于non-trivial类型,调用_Construct(),更安全(使用::operator new()异常处理)
template<bool _TrivialValueTypes>
struct __uninitialized_copy
{
template<typename _InputIterator, typename _ForwardIterator>
static _ForwardIterator
__uninit_copy(_InputIterator __first, _InputIterator __last,
_ForwardIterator __result)
{
_ForwardIterator __cur = __result;
__try
{
for (; __first != __last; ++__first, (void)++__cur)
//直接调用_Construct(::operator new())
std::_Construct(std::__addressof(*__cur), *__first);
return __cur;
}
__catch(...)
{
std::_Destroy(__result, __cur);
__throw_exception_again;
}
}
};
template<>
struct __uninitialized_copy<true>
{
template<typename _InputIterator, typename _ForwardIterator>
static _ForwardIterator
__uninit_copy(_InputIterator __first, _InputIterator __last,
_ForwardIterator __result)
//使用copy(),尽可能优化性能
{ return std::copy(__first, __last, __result); }
};
//copy()接口
//该函数会尽可能的调用memmove(),以优化性能
template<typename _II, typename _OI>
inline _OI
copy(_II __first, _II __last, _OI __result)
{
// concept requirements
__glibcxx_function_requires(_InputIteratorConcept<_II>)
__glibcxx_function_requires(_OutputIteratorConcept<_OI,
typename iterator_traits<_II>::value_type>)
__glibcxx_requires_valid_range(__first, __last);
return (std::__copy_move_a2<__is_move_iterator<_II>::__value>
(std::__miter_base(__first), std::__miter_base(__last),
__result));
}
4.vector结构及方法
SGI STL 中vector的实现位于stl_vector.h
及vector.tcc
中。
vector的实现原理比较简单,维护一个连续线性空间,使用普通指针作为vector的迭代器(Random Access Iterator)。默认使用的空间配置器为std::allocator
。作为”动态数组“,扩容时增大空间以原大小两倍(使用2倍的增长因子的问题在于,每次扩展的新尺寸必然刚好大于之前分配的总和,也就是说,之前分配的内存空间不可能被使用,当前若采用1.5的增长因子在几次扩展之后,可以重用之前的内存空间。显然,k=1.5对缓存更友好,k=2对减少扩容次数更有优势)。
vector的动态增加大小,并不是在原空间之后接续新空间(因为无法保证原空间之后尚有可供配置的空间)而是以增长因子倍数的容量另外配置一块空间,然后将原内容拷贝过来(copy()方法基于型别萃取技术对于trivial 及 non-trivial类型有不同的实现)。然后才开始在原内容之后构造新元素,并释放原空间。
因此,对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效了,需要重点关注。
// Vector implementation -*- C++ -*-
#ifndef _STL_VECTOR_H
#define _STL_VECTOR_H 1
#include <bits/stl_iterator_base_funcs.h>
#include <bits/functexcept.h>
#include <bits/concept_check.h>
#if __cplusplus >= 201103L
#include <initializer_list>
#endif
namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_CONTAINER
//_Vector_base类, 作为vector的基类,提供了构造和析构方法,与Deque的基类_Deque_base相似(bits/stl_deque)
template<typename _Tp, typename _Alloc>
struct _Vector_base
{
//根据_Alloc 及_Tp 作为该容器的特化配置器
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
: public _Tp_alloc_type
{
//维护vector线性空间的指针
pointer _M_start; //当前使用的空间头部
pointer _M_finish; //当前使用的空间尾部
pointer _M_end_of_storage; //当前可用空间的尾部
_Vector_impl()
: _Tp_alloc_type(), _M_start(), _M_finish(), _M_end_of_storage()
{ }
_Vector_impl(_Tp_alloc_type const& __a) _GLIBCXX_NOEXCEPT
: _Tp_alloc_type(__a), _M_start(), _M_finish(), _M_end_of_storage()
{ }
#if __cplusplus >= 201103L
_Vector_impl(_Tp_alloc_type&& __a) noexcept
: _Tp_alloc_type(std::move(__a)),
_M_start(), _M_finish(), _M_end_of_storage()
{ }
#endif
//与另一个vector对象swap的方法
void _M_swap_data(_Vector_impl& __x) _GLIBCXX_NOEXCEPT
{
std::swap(_M_start, __x._M_start);
std::swap(_M_finish, __x._M_finish);
std::swap(_M_end_of_storage, __x._M_end_of_storage);
}
};//end of struct _Vector_impl
public:
typedef _Alloc allocator_type;
//获得_Alloc
_Tp_alloc_type&
_M_get_Tp_allocator() _GLIBCXX_NOEXCEPT
{ return *static_cast<_Tp_alloc_type*>(&this->_M_impl); }
const _Tp_alloc_type&
_M_get_Tp_allocator() const _GLIBCXX_NOEXCEPT
{ return *static_cast<const _Tp_alloc_type*>(&this->_M_impl); }
allocator_type
get_allocator() const _GLIBCXX_NOEXCEPT
{ return allocator_type(_M_get_Tp_allocator()); }
//_Vector_base的构造与析构
_Vector_base()
: _M_impl() { }
_Vector_base(const allocator_type& __a) _GLIBCXX_NOEXCEPT
: _M_impl(__a) { }
_Vector_base(size_t __n)
: _M_impl()
{ _M_create_storage(__n); }
_Vector_base(size_t __n, const allocator_type& __a)
: _M_impl(__a)
{ _M_create_storage(__n); }
#if __cplusplus >= 201103L
_Vector_base(_Tp_alloc_type&& __a) noexcept
: _M_impl(std::move(__a)) { }
_Vector_base(_Vector_base&& __x) noexcept
: _M_impl(std::move(__x._M_get_Tp_allocator()))
{ this->_M_impl._M_swap_data(__x._M_impl); }
_Vector_base(_Vector_base&& __x, const allocator_type& __a)
: _M_impl(__a)
{
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
~_Vector_base() _GLIBCXX_NOEXCEPT
{ _M_deallocate(this->_M_impl._M_start, this->_M_impl._M_end_of_storage
- this->_M_impl._M_start); }
public:
_Vector_impl _M_impl;
pointer
_M_allocate(size_t __n)
{
typedef __gnu_cxx::__alloc_traits<_Tp_alloc_type> _Tr;
return __n != 0 ? _Tr::allocate(_M_impl, __n) : pointer();
}
//析构
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);
}
private:
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类
template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
class vector : protected _Vector_base<_Tp, _Alloc>
{
// 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)
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:
//指针类型及const/reverse iterator
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:
//使用vector_base基类中的空间配置、释放方法
using _Base::_M_allocate;
using _Base::_M_deallocate;
using _Base::_M_impl;
using _Base::_M_get_Tp_allocator;
public:
// 构造/拷贝/析构
// (assign() and get_allocator() are also listed in this section)
//创建一个空vector
vector()
#if __cplusplus >= 201103L
noexcept(is_nothrow_default_constructible<_Alloc>::value)
#endif
: _Base() { }
// @brief 创建一个空vector
// @param __a 一个配置器对象
explicit
vector(const allocator_type& __a) _GLIBCXX_NOEXCEPT
: _Base(__a) { }
#if __cplusplus >= 201103L
// @brief 根据默认构造元素创建vector
// @param __n 初始化元素
// @param __a 一个配置器对象
explicit
vector(size_type __n, const allocator_type& __a = allocator_type())
: _Base(__n, __a)
{ _M_default_initialize(__n); }
// @brief 根据确定的元素数目创建vector
// @param __n 初始化元素的数目
// @param __value _Tp类型的元素
// @param __a 配置器
vector(size_type __n, const value_type& __value,
const allocator_type& __a = allocator_type())
: _Base(__n, __a)
{ _M_fill_initialize(__n, __value); }
#else
explicit
vector(size_type __n, const value_type& __value = value_type(),
const allocator_type& __a = allocator_type())
: _Base(__n, __a)
{ _M_fill_initialize(__n, __value); }
#endif
// @brief vector拷贝构造函数
// @param __x 一个有着确定的元素及配置器的vector
// 新创建的vector使用__x配置器对象的拷贝,所有__x的元素都被拷贝了,
// 但是额外的内存(未使用的)没有被拷贝
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
// @brief Vector移动构造
// @param __x
vector(vector&& __x) noexcept
: _Base(std::move(__x)) { }
/// 使用可变配置器的拷贝构造函数
vector(const vector& __x, const 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());
}
/// 使用可变配置器的移动构造函数
vector(vector&& __rv, const allocator_type& __m)
noexcept(_Alloc_traits::_S_always_equal())
: _Base(std::move(__rv), __m)
{
if (__rv.get_allocator() != __m)
{
this->_M_impl._M_finish =
std::__uninitialized_move_a(__rv.begin(), __rv.end(),
this->_M_impl._M_start,
_M_get_Tp_allocator());
__rv.clear();
}
}
// @brief 根据初始化列表构造vector
// @param __l
// @param __a
// 以初始化列表的拷贝元素创建vector
// 调用该元素类型的拷贝构造函数N(__l.size())次,并且不需要充分配内存
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
/**
* @brief 根据迭代器范围构造vector
* @param __first An input iterator.
* @param __last An input iterator.
* @param __a An allocator.
*
* 如果迭代器为前向、双向、随机存取迭代器,则会调用copy constructorN(last-first)次
* 若使用输入迭代器,则会至多调用2N次copy constructor,并且logN内存重分配
*/
#if __cplusplus >= 201103L
template<typename _InputIterator,
typename = std::_RequireInputIter<_InputIterator>>
vector(_InputIterator __first, _InputIterator __last,
const allocator_type& __a = allocator_type())
: _Base(__a)
{ _M_initialize_dispatch(__first, __last, __false_type()); }
#else
template<typename _InputIterator>
vector(_InputIterator __first, _InputIterator __last,
const allocator_type& __a = allocator_type())
: _Base(__a)
{
// Check whether it's an integral type. If so, it's not an iterator.
typedef typename std::__is_integer<_InputIterator>::__type _Integral;
_M_initialize_dispatch(__first, __last, _Integral());
}
#endif
/**
* dtor仅清除了元素,需明确若元素为指针,所指向的内存不再被读取
*/
~vector() _GLIBCXX_NOEXCEPT
{ std::_Destroy(this->_M_impl._M_start, this->_M_impl._M_finish,
_M_get_Tp_allocator()); }
/**
* @brief vector operator=
* @param __x
* 所有__x中的元素都被复制,但是额外的内存不会被复制,与copy ctor不同的是,
* 配置器不会被复制。
*/
vector&
operator=(const vector& __x);
#if __cplusplus >= 201103L
/**
* @brief %Vector 移动赋值
* @param __x
*/
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;
}
/**
* @brief 重载operator=(vector list)
* @param __l An initializer_list.
* 需明确新得到的vector的size等于赋值的元素数目,旧数据会丢失。
*/
vector&
operator=(initializer_list<value_type> __l)
{
this->assign(__l.begin(), __l.end());
return *this;
}
#endif
/// 获得内存配置器
using _Base::get_allocator;
//vectortcc.cpp 提供了vector常用方法的实现
//reserve():使用_M_allocate_and_copy()重新配置内存并复制入新区间内
template<typename _Tp, typename _Alloc>
void
vector<_Tp, _Alloc>::
reserve(size_type __n)
{
if (__n > this->max_size())
__throw_length_error(__N("vector::reserve"));
if (this->capacity() < __n)
{
const size_type __old_size = size();
//型别萃取
pointer __tmp = _M_allocate_and_copy(__n,
_GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(this->_M_impl._M_start),
_GLIBCXX_MAKE_MOVE_IF_NOEXCEPT_ITERATOR(this->_M_impl._M_finish));
//allocator::_Destroy()
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_finish = __tmp + __old_size;
this->_M_impl._M_end_of_storage = this->_M_impl._M_start + __n;
}
}
//emplace_back(),移动语义的放置(添加)方法,对于某些情景性能提高
#if __cplusplus >= 201103L
template<typename _Tp, typename _Alloc>
template<typename... _Args>
void
vector<_Tp, _Alloc>::
emplace_back(_Args&&... __args)
{
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
{
//使用移动语义
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
std::forward<_Args>(__args)...);
++this->_M_impl._M_finish;
}
else
_M_emplace_back_aux(std::forward<_Args>(__args)...);
}
#endif
//insert()
template<typename _Tp, typename _Alloc>
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();
//若插入位置==end(),并且capacity有余,直接在尾部构造插入
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage
&& __position == end())
{
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish, __x);
++this->_M_impl._M_finish;
}
else
{
#if __cplusplus >= 201103L
const auto __pos = begin() + (__position - cbegin());
//若插入位置==end(),并且capacity有余
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
{
_Tp __x_copy = __x;
_M_insert_aux(__pos, std::move(__x_copy));
}
//若capacity没有剩余,调用辅助的insert()
else
_M_insert_aux(__pos, __x);
#else
_M_insert_aux(__position, __x);
#endif
}
return iterator(this->_M_impl._M_start + __n);
}
//erase()
template<typename _Tp, typename _Alloc>
typename vector<_Tp, _Alloc>::iterator
vector<_Tp, _Alloc>::
_M_erase(iterator __position)
{
//越界判定,调用针对不同型别的move()
if (__position + 1 != end())
_GLIBCXX_MOVE3(__position + 1, end(), __position);
--this->_M_impl._M_finish;
_Alloc_traits::destroy(this->_M_impl, this->_M_impl._M_finish);
return __position;
}
template<typename _Tp, typename _Alloc>
typename vector<_Tp, _Alloc>::iterator
vector<_Tp, _Alloc>::
_M_erase(iterator __first, iterator __last)
{
if (__first != __last)
{
if (__last != end())
_GLIBCXX_MOVE3(__last, end(), __first);
_M_erase_at_end(__first.base() + (end() - __last));
}
return __first;
}
//operator=()
template<typename _Tp, typename _Alloc>
vector<_Tp, _Alloc>&
vector<_Tp, _Alloc>::
operator=(const vector<_Tp, _Alloc>& __x)
{
if (&__x != this)
{
#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不能释放内存
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
//根据__x的大小分配内存
const size_type __xlen = __x.size();
//若__x比容量大,扩容复制赋值
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;
}
//若__x比size小,使用copy()复制/移动
else if (size() >= __xlen)
{
std::_Destroy(std::copy(__x.begin(), __x.end(), begin()),
end(), _M_get_Tp_allocator());
}
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;
}
5.vector优化思路
vector的设计方案及实现技术关键在于其空间配置策略,由之前的分析可知,若vector新增加元素超过当前的capacity, 则容量会“扩充”至原来的2倍。这个扩充过程必须重新配置内存,拷贝移动元素,释放原空间等过程,毫无疑问这很大地影响了性能。
毫无疑问,SGI STL作为成熟的功能库是十分优秀的,但如果我们舍弃一些对某些场景下不太重要的要素,仍有提高的空间。
针对此问题,我们有如下优化思路:
1)改变增长系数,减少扩容、降低配置空间的几率;改变起始容量大小,减小小空间下内存扩展的开销
此项对于某些特定场合,对象大小固定情况有效
2)萃取判定对象类型是否为trivial,例如添加using triviallyrelocatable = std::true_type
之类的标注,对POD类型采用memcpy优化
此项对于当今使用cpp11大量型别萃取特性下的STL优化余地较小
3)判断对象T内部是否存在指向自身或者依赖自身地址的指针.以此使用realloc()
代替常规的malloc()
,减少拷贝数据
此项经测试对于大多数对象有比较好的优化效果,需注意的是realloc()使用时机的判定
4)实现中忽略异常安全,大量使用noexcept
,将异常处理的任务交给使用者
此项经测试对vector提升不明显,对string等存在指向自身指针成员的类提升较明显。需注意的是使用这些容器时,使用者对异常处理任务的把握
5)自定义allocator?memory pool?缓存?(分配固定对象优化?)
此项要实现对于性能、内存的优化不易,但是同第一项相似,对于某些特定的场合,可能有特别的效果。
大部分情况下STL是最优的选择,但是:
vector<bool>
? Feature?Bug?
vector<bool>
不是一个STL容器,因为它不支持一个容器该有的基本操作。禁止有指向单个bit的指针,所以不能取地址;一个容器如果定义了 operator[]
,使用其操作时,应该返回一个对应元素的引用,但是对于vector<bool>
实际上返回的是一个"proxy reference",而不是"true reference"。因为vector<bool>
中不是按Byte存储,而是按bit存储。会造成未定义行为。
所以应禁止使用vector<bool>
,以deque<bool>
代替。
6.使用SGI STL的建议
提前分配固定内存或使用reverse
对于提前知道vector的大小的情形,可以提前分配内存:
vetor<struct> big_array;
big_array[10];
big_array.reserve(N);
for (unsigned int k = 0; k < N; k++)
{
big_array.push_back(k);
}
int sum = total(big_array, N);
return sum;
push_back()
时预留空间不够用:要重新分配内存,并且拷贝当前已有的所有元素到新的内存区域。如果已有元素很多,这个操作将变的非常昂贵。
所以应尽量避免vector重新分配内存,可以预先估计元素的个数,使用reverse()函数进行预留空间的分配。
这个函数会分配一块指定大小的空间,但不进行任何初始化,所以分配出来的空间不包含元素,也就不能访问。然后用同样的方式使用push_back
函数,此时只要不超过之前reserve的空间,vector不会进行内存重新分配,只是简单的依次往后摆放。
使用 shrink_to_fit()
释放 vector 占用的内存, clear()
或 erase()
不会释放内存
在填充或者拷贝到 vector 的时候,应该使用赋值而不是 insert()
或push_back()
.
赋值非常有效率,因为它知道要拷贝的 vector 有多大,然后只需要通过内存管理一次性拷贝 vector 内部的缓存。
所以,想高效填充 vector,首先应尝试使用 assignment,然后再考虑基于迭代器的 insert()
,最后考虑 push_back()
。当然,如果你需要从其它类型的容器拷贝元素到 vector 中,赋值的方式不可行。这种情况下,只好考虑基于迭代器的 insert()
。
将unique_ptr作为vector容器的元素,由于所有权的转移,需要使用std::move()
#include<iostream>
#include<vector>
#include <memory>
using namespace std;
int main()
{
vector<unique_ptr<int>> vec;
unique_ptr<int> sp(new int(126));
//vec.push_back(1);
vec.push_back(std::move(sp));//尝试引用已删除的函数
cout << *vec[0]<< endl; // 输出126
//cout << *sp << endl;
return 0;
}
当然将vector<unique_ptr<int>> vec
作为函数参数也会产生所有权转移问题,需要按引用、或是const引用传递即可,就不涉及到所有权的转移问题