zoukankan      html  css  js  c++  java
  • 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个关键迭代器。

  • 相关阅读:
    放置消息
    MQ基础概念和介绍
    jQuery之双下拉框
    Spring整合JMS——事务管理
    DataTable学习笔记---排序细则、列隐藏
    jquery datatables api
    JavaScript高级 面向对象(2)--调试工具的使用:音乐循环播放
    JavaScript高级 面向对象(1)--添加一个div标签
    VBA学习笔记(9)--生成点拨(1)
    VBA学习笔记(8)--遍历所有文件夹和文件
  • 原文地址:https://www.cnblogs.com/vlyf/p/11838458.html
Copyright © 2011-2022 走看看