STL源码剖析笔记

第二章 空间配置器(allocator)

空间配置器标准接口(allocator)

allocator配置器是SGI STL提供的标准接口,但它只是对::operator new()和::operator delete()做了一层简单封装,效率不高,所以SGI并没有使用它。

高效的空间配置器结构std::alloc

class Foo{...};
Foo* pf = new Foo;
delete pf;

这里的new内部包括两步操作:

  • 调用::operator new()分配内存
  • 调用Foo的构造函数

delete也包括两步操作:

  • 调用Foo的析构函数
  • 调用::operator delete()释放内存

上面的operator new()里面其实调用了malloc(),operator delete()里面调用了delete()

为了提高这个过程的效率,STL allocator将这两个阶段的操作区分开来,分为内存操作和对象操作:

  1. 内存操作
    • 内存分配:alloc::allocator()
    • 内存释放:alloc::deallocator()
  2. 对象操作
    • 对象构造:::construct() -> placement new
    • 对象析构:::destroy()

空间配置器std::alloc定义在stl_alloc.h中,整个配置器分为两层,为了考虑小型区块可能造成的内部碎片问题,SGI设计了两层级配置器,第一级采用malloc()free(),第二级则视情况而定:若配置区块超过128bytes,则采用第一级配置器;否则就采用memory pool的方式即第二级配置器。而整个容器的allocator就是simple_alloc,其实是对std::alloc的简单封装,使之能符合STL标准。

typedef __malloc_alloc_template<0> malloc_alloc;	// 第一级配置器
typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;	// 第二级配置器,默认

template<class T, class Alloc>
class simple_alloc {

public:
    static T *allocate(size_t n)
                { return 0 == n? 0 : (T*) Alloc::allocate(n * sizeof (T)); }
    static T *allocate(void)
                { return (T*) Alloc::allocate(sizeof (T)); }
    static void deallocate(T *p, size_t n)
                { if (0 != n) Alloc::deallocate(p, n * sizeof (T)); }
    static void deallocate(T *p)
                { Alloc::deallocate(p, sizeof (T)); }
};

上面的模板参数Alloc就表示了配置器的种类,一级或二级。

一级配置器

template <int __inst>
class __malloc_alloc_template {
private:
  //用来处理内存不足的情况
  static void* _S_oom_malloc(size_t);
  static void* _S_oom_realloc(void*, size_t);
#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
  static void (* __malloc_alloc_oom_handler)();
#endif

public:
  static void* allocate(size_t __n)
  {
    void* __result = malloc(__n);
    if (0 == __result) __result = _S_oom_malloc(__n);
    return __result;
  }

  static void deallocate(void* __p, size_t /* __n */)
  {
    free(__p);
  }

  static void* reallocate(void* __p, size_t /* old_sz */, size_t __new_sz)
  {
    void* __result = realloc(__p, __new_sz);
    if (0 == __result) __result = _S_oom_realloc(__p, __new_sz);
    return __result;
  }
};

// malloc_alloc out-of-memory handling
#ifndef __STL_STATIC_TEMPLATE_MEMBER_BUG
template <int __inst>
void (* __malloc_alloc_template<__inst>::__malloc_alloc_oom_handler)() = 0;
#endif

可以看出,一级配置器malloc_alloc仅仅是对malloc和free的简单封装,这里的__malloc_alloc_oom_handler函数指针默认为NULL,它类似于C++new里面的set_new_handler()函数,就是在空间分配失败时,会调用这个回调函数。

二级配置器

template <bool threads, int inst>
class __default_alloc_template {
private:

  //获取bytes最适合的块大小,比如7->8, 9->16, 22->24
  static size_t
  _S_round_up(size_t __bytes) 
    { return (((__bytes) + (size_t) _ALIGN-1) & ~((size_t) _ALIGN - 1)); }

__PRIVATE:
  union _Obj {
        union _Obj* _M_free_list_link;
        char _M_client_data[1];
  };
private:
  static _Obj* __STL_VOLATILE _S_free_list[]; 
  static  size_t _S_freelist_index(size_t __bytes) {
        return (((__bytes) + (size_t)_ALIGN-1)/(size_t)_ALIGN - 1);
  }
  //返回一个size为__n的对象,然后加入到对应的free list
  static void* _S_refill(size_t __n);
  //配置一大块内存
  static char* _S_chunk_alloc(size_t __size, int& __nobjs);
  //内存池起始位置和结束位置
  static char* _S_start_free;
  static char* _S_end_free;
  static size_t _S_heap_size;

# ifdef __STL_THREADS
    //多线程环境会用到的锁
    static _STL_mutex_lock _S_node_allocator_lock;
# endif
    class _Lock;
    friend class _Lock;
    class _Lock {
        public:
            _Lock() { __NODE_ALLOCATOR_LOCK; }
            ~_Lock() { __NODE_ALLOCATOR_UNLOCK; }
    };
public:
  static void* allocate(size_t __n);
  static void deallocate(void* __p, size_t __n);
  static void* reallocate(void* __p, size_t __old_sz, size_t __new_sz);
} ;

二级配置器的设计是为了对付小型区块内部碎片,为了管理区块,还需要一些额外内存存放管理信息cookie。它维护了一个链表数组,每个节点指向大小不同的内存块,大小为8、16、24...128byte。每次分配内存时,就选择合适大小的块分配。如果块不够,就从内存池中分配一大块内存,切成小块,加入到对应的链表中。如果内存池也不够用了,就调用malloc来补充内存池。

allocator

static void* allocate(size_t __n)
  {
    void* __ret = 0;
    //如果大于128byte,用第一级配置器
    if (__n > (size_t) _MAX_BYTES) {
      __ret = malloc_alloc::allocate(__n);
    }
    else {
      _Obj* __STL_VOLATILE* __my_free_list
          = _S_free_list + _S_freelist_index(__n);

      //如果对应的free_list为空,那么就refill,否则就调整一下freelist头节点,然后返回之
      _Obj* __RESTRICT __result = *__my_free_list;
      if (__result == 0)
        __ret = _S_refill(_S_round_up(__n));
      else {
        *__my_free_list = __result -> _M_free_list_link;
        __ret = __result;
      }
    }
    return __ret;
  };

allocator函数先判断是否调用一级配置器。如果不是,就选择一个合适块大小的链表,然后分配一个块。如果链表为空,就refill填充内存。

refill

template <bool __threads, int __inst>
void*
__default_alloc_template<__threads, __inst>::_S_refill(size_t __n)
{
    int __nobjs = 20;
    char* __chunk = _S_chunk_alloc(__n, __nobjs);
    _Obj* __STL_VOLATILE* __my_free_list;
    _Obj* __result;
    _Obj* __current_obj;
    _Obj* __next_obj;
    int __i;

    if (1 == __nobjs) return(__chunk);
    __my_free_list = _S_free_list + _S_freelist_index(__n);

    /* Build free list in chunk */
      __result = (_Obj*)__chunk;
      *__my_free_list = __next_obj = (_Obj*)(__chunk + __n);
      for (__i = 1; ; __i++) {
        __current_obj = __next_obj;
        __next_obj = (_Obj*)((char*)__next_obj + __n);
        if (__nobjs - 1 == __i) {
            __current_obj -> _M_free_list_link = 0;
            break;
        } else {
            __current_obj -> _M_free_list_link = __next_obj;
        }
      }
    return(__result);
}

refill先调用chunk_alloc去内存池分配20个块大小的内存。如果只返回一个块,就还是返回。否则就把分配来的内存切成小块,加入到对应块大小的链表中。

chunk_alloc

template <bool __threads, int __inst>
char*
__default_alloc_template<__threads, __inst>::_S_chunk_alloc(size_t __size, 
                                                            int& __nobjs)
{
    char* __result;
    size_t __total_bytes = __size * __nobjs;
    size_t __bytes_left = _S_end_free - _S_start_free;
	// 内存池容量可以满足需求
    if (__bytes_left >= __total_bytes) {
        __result = _S_start_free;
        _S_start_free += __total_bytes;
        return(__result);
    }
    // 可以满足一个block大小,就也返回
    else if (__bytes_left >= __size) {
        __nobjs = (int)(__bytes_left/__size);
        __total_bytes = __size * __nobjs;
        __result = _S_start_free;
        _S_start_free += __total_bytes;
        return(__result);
    } else {

      //如果一个block都无法提供,那么就需要重新为内存池分配内存
      //分配的大小为 两倍需求量 + 随分配次数而增加的附加量(heap_size初始为0)
        size_t __bytes_to_get = 
      2 * __total_bytes + _S_round_up(_S_heap_size >> 4);
        // Try to make use of the left-over piece.

        //如果内存池还有一丁点剩下的内存,就把他加入到对应的free list,尽量节约
        if (__bytes_left > 0) {
            _Obj* __STL_VOLATILE* __my_free_list =
                        _S_free_list + _S_freelist_index(__bytes_left);

            ((_Obj*)_S_start_free) -> _M_free_list_link = *__my_free_list;
            *__my_free_list = (_Obj*)_S_start_free;
        }
        _S_start_free = (char*)malloc(__bytes_to_get);


        //如果malloc分配失败
        if (0 == _S_start_free) {
            size_t __i;
            _Obj* __STL_VOLATILE* __my_free_list;
        _Obj* __p;
            // Try to make do with what we have.  That can't
            // hurt.  We do not try smaller requests, since that tends
            // to result in disaster on multi-process machines.
            for (__i = __size;
                 __i <= (size_t) _MAX_BYTES;
                 __i += (size_t) _ALIGN) {
                __my_free_list = _S_free_list + _S_freelist_index(__i);
                __p = *__my_free_list;
                if (0 != __p) {
                    *__my_free_list = __p -> _M_free_list_link;
                    _S_start_free = (char*)__p;
                    _S_end_free = _S_start_free + __i;
                    return(_S_chunk_alloc(__size, __nobjs));
                    // Any leftover piece will eventually make it to the
                    // right free list.
                }
            }
        _S_end_free = 0;    // In case of exception.
            _S_start_free = (char*)malloc_alloc::allocate(__bytes_to_get);
            // This should either throw an
            // exception or remedy the situation.  Thus we assume it
            // succeeded.
        }
        _S_heap_size += __bytes_to_get;
        _S_end_free = _S_start_free + __bytes_to_get;
        return(_S_chunk_alloc(__size, __nobjs));
    }
}

chunk_alloc先是判断自己的内存池里面的内存还够不够这次分配。如果够,就正常处理下,然后返回。如果只够一个block,也会返回。但是如果一个block也不够了呢?这时候就需要malloc了,具体要申请的是2 * __total_bytes + _S_round_up(_S_heap_size >> 4);,这个total_bytes好理解,就是我们需要的空间,而这个heap_size则是这个class template中的一个static data member,存储的是迄今为止用了多少heap memory,也就是malloc了多少内存,初始值为0。所以说,每次要申请的内存是两倍的需求量加上,一点点附加量。然后,malloc会比需求多点,给内存池存点余额,避免频繁的调用malloc,影响速度。而malloc失败后,会尝试把链表里面的mem block都收集到内存池中去,然后再去尝试满足这次分配。然后就是调用malloc_alloc

第三章 iterator and traits

iterator

迭代器是一种行为类似指针的对象,本文介绍iterator与traits的关系,以及对traits内容的补充。包含stl_iterator.h的部分内容,并用c++11对其进行略微改写。
上篇文章已经介绍了这五种类型的特征,它们只是为了激活重载机制而设定,并不需要其他成员。它们的定义如下:

//五种迭代器类型
struct input_iterator_tag{};
struct output_iterator_tag{};
struct forward_iterator_tag: public input_iterator_tag { };
struct bidirectional_iterator_tag: public forward_iterator_tag{ };
struct random_access_iterator_tag: public bidirectional_iterator_tag{ };

接下来是iterator的定义:

//自造的iterator最好继承下面的std::iterator
template<typename Category, typename T, 
         typename Distance = ptrdiff_t, 
         typename Pointer = T*,
         typename Reference = T&>
struct iterator
{
    using iterator_category = Category;
    using value_type = T;
    using difference_type = Distance;
    using pointer = Pointer;
    using reference = Reference;
};

//萃取机Traits
//萃取出迭代器的特性
template<typename Iterator>
struct iterator_traits
{
    using iterator_category = typename Iterator::iterator_category;
    using value_type = typename Iterator::value_type;
    using difference_type = typename Iterator::difference_type;
    using pointer = typename Iterator::pointer;
    using reference = typename Iterator::reference;
};
//针对原生指针的片特化版本
template<typename T>
struct iterator_traits<T*>
{
    using iterator_category = random_access_iterator_tag;
    using value_type = T;
    using difference_type = ptrdiff_t;
    using pointer = T*;
    using reference = T&;
};
//pointer-to-const
template<typename T>
struct iterator_traits<const T*>
{
    using iterator_category = random_access_iterator_tag;
    using value_type = T;
    using difference_type = ptrdiff_t;
    using pointer = const T*;
    using reference = const T&;
};
//确定某个迭代器类型,并返回该类型的一个迭代器对象,方便进行函数重载
template<typename Iterator>
inline typename iterator_traits<Iterator>::iterator_category
iterator_category(const Iterator&)
{
    using category = typename iterator_traits<Iterator>::iterator_category;
    return category();
}
//确定某个迭代器的distance type
template<typename Iterator>
inline typename iterator_traits<Iterator>::difference_type*
distance_type(const Iterator&)
{
    return static_cast<typename iterator_traits<Iterator>::difference_type*>(nullptr);
}
//确定某个迭代器的value type
template<typename Iterator>
inline typename iterator_traits<Iterator>::value_type*
value_type(const Iterator&)
{
    return static_cast<typename iterator_traits<Iterator>::value_type*>(nullptr);
}

//整个distance函数的实现,第三个参数只是激活重载机制,无其他用处
template<typename InputIterator>
inline typename iterator_traits<InputIterator>::difference_type
_distance(InputIterator first, InputIterator last, input_iterator_tag)
{
    typename iterator_traits<InputIterator>::difference_type n = 0;
    while(first != last)
    {
        ++first;
        ++n;
    }
}

template<typename RandomAccessIterator>
inline typename iterator_traits<RandomAccessIterator>::difference_type
_distance(RandomAccessIterator first, RandomAccessIterator last, random_access_iterator_tag)
{
    return last - first;
}
//对外接口,适应不同类型的迭代器
template<typename InputIterator>
inline typename iterator_traits<InputIterator>::difference_type
distance(InputIterator first, InputIterator last)
{
    //先利用traits确定迭代器的类型
    using category = typename iterator_traits<InputIterator>::iterator_category;
    //利用函数重载,第三个参数只是激活重载机制,无其他用处
    _distance(first, last, category());
}

//整个advance函数的实现,第三个参数只是激活重载机制,无其他用处
template<typename InputerIterator, typename Distance>
inline void _advance(InputerIterator& i, Distance n, input_iterator_tag)
{
    while(n--) ++i;
}

template<typename BidirectionalIterator, typename Distance>
inline void _advance(BidirectionalIterator i, Distance n, bidirectional_iterator_tag)
{
    if(n >= 0)
        while(n--) ++i;
    else
        while(n++) --i;
}

template<typename RandomAccessIterator, typename Distacne>
inline void _advance(RandomAccessIterator& i, Distacne n, random_access_iterator_tag)
{
    i += n;
}

//对外接口,适应不同类型的迭代器
template<typename InputIterator, typename Distance>
inline void advance(InputIterator& i , Distance n)
{
    _advance(i, n, iterator_category(i));
}

#endif //STL_ITERATOR_H

主要讨论如何获取迭代器相应型别。使用迭代器时,很可能用到其型别,若需要声明某个迭代器所指对象的型别的变量,该如何解决。方法如下:

function template的参数推导机制

例如:

template<typename I, typename T>
void func_impl(I iter, T t)
{
    //T为迭代器所指对象的型别
    T tmp;
    //.....
}

template<typename I>
inline
void func(I iter)
{   
    //func工作交给func_impl完成
    func_impl(iter, *iter);
}

int main()
{
    int i;
    func(&i);

    return 0;
}

func_impl()是一个 function template,一旦被调用,编译器会自动进行template参数推导,从而导出型别T,无需自己指出型别,解决问题。迭代器相应型别不只是迭代器所指对象的型别一种而已,最常用的相应型别有五种,但并非任何情况都可利用上述template参数推导机制来取得。这就需要其他方法。

Traits编程技法

迭代器所指对象的型别,成为该迭代器的value type,上述模板参数推导并非全面可用,在需要value type作为函数返回值时,就不能解决了。template参数推导的只是参数而已。因此,声明内嵌型别就出现了。

例如:

template<typename T>
struct MyIter
{
    typedef T value_type;   //内嵌型别声明
    T* ptr;
    MyIter(T* p = nullptr):ptr(p) { }
    T& operator*() const { return *ptr;}
    //...
};

template<typename I>
typename I::value_type  //函数func()的返回类型,为I类型迭代器中的value_type
func(I ite)
{
    return *ite;
}
int main()
{
	MyIter<int> ite(new int(8));
    cout<<func(ite);    //输出8
    return 0;
}

func()函数的返回值必须加上关键字typename,用来告诉编译器这时一个模板类型。但并不是所有迭代器都为class type,原生指针就不是,它就不能定义内嵌型别。这时模板偏特化(template partial specialization)就能解决这个问题。

偏特化的意义

如果class template拥有一个以上的template参数,我们可以针对其中某个(或数个,但并非全部)template参数进行特化工作。也就是将泛化版本中的某些template参数给予明确的指定。

如:

template<typename U, typename V, typename T>
class C { };

偏特化不是template参数U、V或T指定某个参数值,而是针对(任何)template参数更进一步的条件限制所设计出来的一个特化版本。看这个例子:

//泛化版本
template<typename T>
class C { }
//偏特化版本
template<typename T>
class C<T*> { };	//解决原生指针的问题

如此便能解决前面的内嵌型别的问题。下面这个class template专门用来萃取迭代器的特性之一 :value_type

template<typename I>
struct Iterator_traits	//traits指的是特性
{
    typedef typename I::value_type value_type;
};

如果I内定义了自己的value type,通过这个traits萃取出来的value type就是I::value_type,则前面的func(),可改为:

template<typename I>
typename Iterator_traits<I>::value_type
func(I ite)
{
    return *ite;
}

现在写出Iterator_traits的一个偏特化版本,这就解决了原生指针的问题,如果写成Iterator_traits<int*>::value_type,便得到int:

template<typename T>
struct Iterator_traits<T*>
{
    typedef T value_type;
};

但针对“指向常数对象的指针”,Iterator_traits<const int*>::value_type可萃取到const int,此时若要得到non-const int,就要设计下面这一个偏特化版本:

template<typename T>
struct Iterator_traits<const T*>
{
    tpyedef T value_type;   //当迭代器为pointer-to-const,得到non-const T
};

现在迭代器MyIter、原生指针int*或const int *,都能通过Iterator_traits取出正确的value type。
所以,每个迭代器应以内嵌型别定义的方式定义出相应型别,以遍traits运作。

迭代器相应型别

value type

value type指迭代器所指对象的型别。

difference type

difference type表示两个迭代器之间的距离,也能表示一个容器的最大容量,对连续的空间而言,头尾的距离就为最大容量。如STL的count(),其返回值就必须使用迭代器的difference type:

template<typename I, typename T>
typename iterator_traits<I>::difference_type    //函数的返回值类型
count(I first, I last, const T& value)
{
    typename iterator_traits<I>::difference_type n = 0; //记录个数
    for( ; first != last; ++first)
    {
        if(*first == value)
            ++n;
    }
    return n;
};

针对原生指针而写的偏特化版本,以c++内建的ptrdiff_t(定义于cstddef头文件)作为原生指针的difference type:

template<typename I>
struct iterator_traits
{
    //...
    typedef typename I::difference_type difference_type;
};
//原生指针的偏特化版本
template<typename T>
struct iterator_traits<T*>
{
    typedef ptrdiff_t difference_type;
};
//pointer-to-const的偏特化版本
template<typename T>
struct iterator_traits<const T*>
{
    typedef ptrdiff_t difference_type;
};

现在,我们可以通过写

typename iterator_traits<I>::difference_type

来得到任何迭代器I的difference_type。

reference type

引用类型,传回一个迭代器所指对象的引用

pointer type

传回一个,指向迭代器所指之物的pointer。

Item& operator*() const { return *ptr; }
Item* operator->() const { return ptr; }

Item&便是某个迭代器的reference type,而Item*便是其pointer type。在traits内:

template<typename I>
struct iterator_traits
{
    typedef typename I::pointer pointer;
    typedef typename I::reference reference;
};
//针对原生指针的偏特化版本
template<typename T>
struct iterator_traits<T*>
{
    typedef T* pointer;
    typedef T& reference;
};
//针对pointer-to-const的偏特化版本
template<typename T>
struct iterator_traits<const T*>
{
    typedef const T* pointer;
    typedef const T& reference;
};

iterator_category

根据移动特性,迭代器被分为五类:

  • Input Iterator: read only
  • Output Iterator: write only
  • Forward Iterator: 允许写入型算法在这种迭代器所形成的区间上进行读写操作
  • Bidirectional Iterator: 可双向移动
  • Random Access Iterator: 涵盖所有指针算数能力,包括p+n,p-n,p[n]等等

它们从上到下,功能依次强化。
下面以advance()函数为例,该函数有两个参数迭代器p,数值n;函数内部将p累进n次。
下面针对三种不同迭代器进行示范:

//InputIterator
template<typename InputIterator, typename Distance>
void advance_II(InputIterator& i, Distance n)
{
    //单向逐一前进
    while(n--) ++i;
}
//BidirectionalIterator
template<typename BidirectionalIterator, typename Distance>
void advance_BI(BidirectionalIterator& i, Distance n)
{
    //双向逐一前进
    if(n >= 0)
        while(n--) ++i;
    else
        while(n++) --i;
}
//RandomAccessIterator
template<typename RandomAccessIterator, typename Distance>
void advance_RAI(RandomAccessIteraor& i, Distance n)
{
    //双向跳跃前进
    i += n;
}
//封装版本
template<typename InputIterator, typename Distance>
void advance(InputIterator& i, Distance n)
{
    //分别判断迭代器类型
    if(is_random_access_iterator(i))
        advance_RAI(i, n);
    else if(is_bidirectionl_iterator(i))
        advance_BI(i, n);
    else
        advance_II(i, n);
}

这样在执行期才决定使用哪个版本,会影响效率。最好能在编译器就选择正确版本,重载函数机制就解决了这个问题。
前面3个advance_xx()都有两个template 参数,类型不确定,为了形成重载函数,必须加上一个型别已经确定的函数参数。下面五个classes代表了五种迭代器类型:

//标记用的型别
struct input_iterator_tag { };
struct output_iterator_tag { };
struct forward_iterator_tag: public input_iterator_tag { };
struct bidirectional_iterator_tag: public forward_iterator_tag { };
struct random_access_iterator_tag: public bidirectional_iterator_tag { };

这些classes当做标记用,作为第三个参数,使能达到重载函数的目的:

//InputIterator
template<typename InputIterator, typename Distance>
void _advance(InputIterator& i, Distance n, input_iterator_tag)
{
    //单向逐一前进
    while(n--) ++i;
}
//ForwardIterator
template<typename ForwardIterator, typenmae Distance>
void _advance(ForwardIterator& i, Distance n, forward_iterator_tag)
{
    _advance(i, n, input_iterator_tag())
}
//BidirectionalIterator
template<typename BidirectionalIterator, typename Distance>
void _advance_BI(BidirectionalIterator& i, Distance n, bidirectional_iterator_tag)
{
    //双向逐一前进
    if(n >= 0)
        while(n--) ++i;
    else
        while(n++) --i;
}
//RandomAccessIterator
template<typename RandomAccessIterator, typename Distance>
void advance_RAI(RandomAccessIteraor& i, Distance n, random_access_iterator_tag)
{
    //双向跳跃前进
    i += n;
}

每一个_advance()的最后一个参数只声明型别,是为了激活重载函数机制,并不需要参数名称,并且函数中并不使用该参数。下面是对外开放的接口,以调用不同的_advance()。

template<typename InputIterator, typename Distance>
inline void advance(InputIterator& i, Distance n)
{
    _advance(i, n, iterator_traits<InputIterator>::iterator_category());
}
//iterator_category()在stl_iterator.h中定义
template<typename I>
inline typename iterator_traites<I>::iterator_category
iterator_category(const I&)
{
    typedef typename iterator_traits<I>:iterator_category category;
    return category();
}

为满足上述行为,traits中增加相应型别:

template<typename I>
struct iterator_traits
{
    //...
    typedef typename I::iterator_category iterator_category;
};
//针对原生指针的偏特化版本
template<typename T>
struct iterator_traits<T*>
{
    //...
    //原生指针为一种RandomAccessIterator
    typedef random_access_iterator_tag iterator_category;
};
//针对pointer-to-const的偏特化版本
template<typename T>
struct iterator_traits<const T*>
{
    //...
    typedef randow_access_iterator_tag iterator_category;
};

任何一个迭代器,它的类型应属于迭代器类型中功能最强大的类型如int* ,既是Random又是Bidirectional,既是Forward又是Input,他应为Random。

第四章 序列式容器

vector

概述

vector是动态数组,其底层空间会随着插入元素而自动扩充,并且是连续的内存。如果空间不够用,会扩充至两倍空间,会申请更大的一片空间,将原数据copy过去,并释放旧空间;如果两倍都不够用,比如空间大小为4,但要插入100个元素,那么就需要让空间增长n个大小因此如果空间被重新配置,那么原来的所有迭代器都会失效,这点尤为重要。

结构

template <class T, class Alloc = alloc>
class vector {
public:
  typedef T value_type;
  typedef value_type* pointer;
  typedef const value_type* const_pointer;
  typedef value_type* iterator;
  typedef const value_type* const_iterator;
  typedef value_type& reference;
  typedef const value_type& const_reference;
  typedef size_t size_type;
  typedef ptrdiff_t difference_type;
  // ...
protected:
  typedef simple_alloc<value_type, Alloc> data_allocator;
  iterator start;
  iterator finish;
  iterator end_of_storage;
  void insert_aux(iterator position, const T& x);
  void deallocate() {
    if (start) data_allocator::deallocate(start, end_of_storage - start);
  }
  // ...
  ~vector() { 
    destroy(start, finish);
    deallocate();
  }
};

vector使用的空间配置器为simple_allocstartfinish之间是已经使用的空间,finishend_of_storage直接是预留的未使用空间,每次当预留的空间都不足时,就会申请一块新的连续空间,将数据copy过去,并释放旧空间,这通过deallocate()完成。

常用函数

  iterator begin() { return start; }
  iterator end() { return finish; }
  size_type size() const { return size_type(end() - begin()); }
  size_type capacity() const { return size_type(end_of_storage - begin()); }
  bool empty() const { return begin() == end(); }
  reference operator[](size_type n) { return *(begin() + n); }
  const_reference operator[](size_type n) const { return *(begin() + n); }

  vector() : start(0), finish(0), end_of_storage(0) {}
  vector(size_type n, const T& value) { fill_initialize(n, value); }
  vector(int n, const T& value) { fill_initialize(n, value); }
  vector(long n, const T& value) { fill_initialize(n, value); }
  explicit vector(size_type n) { fill_initialize(n, T()); }

需要注意的是size()capacity()的区别,前者是已存储数据的个数,后者是总的可用空间的大小。几个构造函数是通过fill_initialize()实现的,如果是POD类型的对象,则就和memset()相似,因为不需要调用构造函数,vector析构的时候也不需要调用每个对象的析构函数。如果对象有nontrivial constructor,才会调用构造和析构函数。

void reserve(size_type n) {
    if (capacity() < n) {
      const size_type old_size = size();
      iterator tmp = allocate_and_copy(n, start, finish);
      destroy(start, finish);
      deallocate();
      start = tmp;
      finish = tmp + old_size;
      end_of_storage = start + n;
    }
}

void resize(size_type new_size, const T& x) {
    if (new_size < size()) 
      erase(begin() + new_size, end());
    else
      insert(end(), new_size - size(), x);
}
void resize(size_type new_size) { resize(new_size, T()); }

void swap(vector<T, Alloc>& x) {
    __STD::swap(start, x.start);
    __STD::swap(finish, x.finish);
    __STD::swap(end_of_storage, x.end_of_storage);
}

reserve()是相对于capacity()来操作的,就是扩容总的存储空间,通过allocator new area -> copy old data to new area -> destroy old data -> deallocate old memory

resize()是相对于size()来操作的,就是调整已存在对象的个数。

swap()是通过交换内部的3个迭代器实现的,这很高效,并不是复制对象再释放内存。

insert

void push_back(const T& x) {
    if (finish != end_of_storage) {
      construct(finish, x);
      ++finish;
    }
    else
      insert_aux(end(), x);
}

iterator insert(iterator position, const T& x) {
    size_type n = position - begin();
    if (finish != end_of_storage && position == end()) {
      construct(finish, x);
      ++finish;
    }
    else
      insert_aux(position, x);
    return begin() + n;
}
template <class T, class Alloc>
void vector<T, Alloc>::insert(iterator position, size_type n, const T& x) {
  if (n != 0) {
    if (size_type(end_of_storage - finish) >= n) {
      T x_copy = x;
      const size_type elems_after = finish - position;
      iterator old_finish = finish;
      if (elems_after > n) {
        uninitialized_copy(finish - n, finish, finish);
        finish += n;
        copy_backward(position, old_finish - n, old_finish);
        fill(position, position + n, x_copy);
      }
      else {
        uninitialized_fill_n(finish, n - elems_after, x_copy);
        finish += n - elems_after;
        uninitialized_copy(position, old_finish, finish);
        finish += elems_after;
        fill(position, old_finish, x_copy);
      }
    }
    else {
      const size_type old_size = size();        
      const size_type len = old_size + max(old_size, n);
      iterator new_start = data_allocator::allocate(len);
      iterator new_finish = new_start;
      __STL_TRY {
        new_finish = uninitialized_copy(start, position, new_start);
        new_finish = uninitialized_fill_n(new_finish, n, x);
        new_finish = uninitialized_copy(position, finish, new_finish);
      }
#         ifdef  __STL_USE_EXCEPTIONS 
      catch(...) {
        destroy(new_start, new_finish);
        data_allocator::deallocate(new_start, len);
        throw;
      }
#         endif /* __STL_USE_EXCEPTIONS */
      destroy(start, finish);
      deallocate();
      start = new_start;
      finish = new_finish;
      end_of_storage = new_start + len;
    }
  }
}

插入单个元素,都是先判断容量是否够,如果不够就会调insert_aux()

insert()也是先判断备用空间,如果充足,就将从position开始的元素后移,再将插入元素copy进来。如果备用空间不足,就先决定新的空间大小,这根据插入个数n来决定是旧长度的2倍还是旧长度+n:

      const size_type old_size = size();        
      const size_type len = old_size + max(old_size, n);

erase

void pop_back() {
    --finish;
    destroy(finish);
  }
  iterator erase(iterator position) {
    if (position + 1 != end())
      copy(position + 1, finish, position);
    --finish;
    destroy(finish);
    return position;
  }
  iterator erase(iterator first, iterator last) {
    iterator i = copy(last, finish, first);
    destroy(i, finish);
    finish = finish - (last - first);
    return first;
  }

pop_back()erase()就比较简单了,只是destroy()和调整那3个关键迭代器。

原文地址:https://www.cnblogs.com/vlyf/p/11838458.html