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个关键迭代器。

  • 相关阅读:
    Java实现 LeetCode 455 分发饼干
    Java实现 LeetCode 455 分发饼干
    Java实现 LeetCode 455 分发饼干
    Java实现 LeetCode 454 四数相加 II
    Java实现 LeetCode 454 四数相加 II
    Java实现 LeetCode 454 四数相加 II
    FFmpeg解码H264及swscale缩放详解
    linux中cat more less head tail 命令区别
    C语言字符串操作总结大全(超详细)
    如何使用eclipse进行嵌入式Linux的开发
  • 原文地址:https://www.cnblogs.com/vlyf/p/11838458.html
Copyright © 2011-2022 走看看