STL源码剖析之allocator&vector

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,但其只是对newdelete的简单封装。

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.hvector.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引用传递即可,就不涉及到所有权的转移问题

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值