zoukankan      html  css  js  c++  java
  • STL源码阅读(一)

    STL源码阅读(一) (SGI STL v3.3)

    STL提供六大组件:容器(containers),算法(algorithms),迭代器(iterators),仿函数(functors),
    适配器(adapters),分配器(allocators)。

    SGI分配器设计目标:
    - 向system heap申请空间
    - 考虑多线程情况
    - 考虑内存不足时应变措施
    - 考虑过多的小型内存区域可能造成的内存碎片情况

    对于小型区域可能造成的内存碎片问题,SGI设计了两级分配器。

    template <int __inst>
    class __malloc_alloc_template

    当内存大小大于128字节时,视为内存足够大,
    使用一级分配器,使用malloc,free分配内存与释放内存,模拟C++ set_new_handler以处理内存不足的情况(
    内存分配不足时,调用指定的处理函数,该函数尝试释放内存,然后再分配内存,一直到内存分配成功为止);

    template <bool threads, int inst>
    class __default_alloc_template

    当内存小于128字节时,使用二级分配器不同的策略管理内存。维护16个自由链表,负责16个小内存区域的分配能力。
    内存池以malloc分配而得,当内存不足,转调用一级分配器。如果需求内存区域大于128字节,转调用一级分配器。

    迭代器类型:Input Iterator(只读),Output Iterator(只写),Forward Iterator(读写), Bidirectional Iterator,
    Random Access Iterator。前3中只支持operator++,第4中支持operator++和operator–,第5中支持p+n、p-n、
    p[n]、p1 - p2和p1 < p2。

    C++11将POD(Plain Old data)划分为两个基本概念,只有满足这两个基本类型才能成为POD类型:
    - 平凡的(trivial)。一个trivial class 或struct应满足一下条件,拥有平凡的默认构造函数和析构函数(编译器自动生成的);
    拥有平凡的默认复制构造函数和平凡的移动构造函数;拥有平凡的复制赋值运算符和移动赋值运算符;不能包含虚函数和虚基类;
    - 标准布局的(standard layout)。所有非静态成员都有相同的访问权限;在类继承时,满足:派生类中有非静态成员,且仅有
    一个仅包含静态成员的基类。基类有非静态成员,而派生类没有非静态成员;类中第一个非静态成员和其基类不同;没有虚函数和
    虚基类;所有非静态数据成员均符合标准布局类型,其基类也符合标准布局。

    POD的优点:
    1.字节赋值, 代码中我们可以安全的使用memset和memcpy函数对POD类型进行初始化和拷贝等操作.

    2.提供了对C内存布局的兼容. C++程序可以与C函数 进行相互的操作, 因为POD类型的数据在C++与C之间的操作总是安全的.

    3.保证了静态初始化的安全有效, 静态初始化在很多时候能够提高程序的性能, 而POD类型的对象初始化往往更加简单(比如放在目标文件的.bss段 , 在初始化中直接被赋0).

    SGI STL的各种容器
    Sequence Containers:array (built-in), vector, heap (in the form of algorithm),priority_queue, list, slist (non-standard), deque, stack (adapter), queue (adapter)。
    Associate Containers: RB-tree (inner use),set, map, multiset, multimap,
    hashtable (non-standard), hash_set (non-standard), hash_map (non-standard), hash_multiset (non-standard), hash_multimap (non-standard)。

    vector的实现技术,关键在于其对大小的控制以及重新移动时的数据移动的效率。
    vector维护的是一个连续的内存空间。注意:如果增加元素时,超过当时的容量,则容量会自动扩充至两倍。如果容量还不充足,知道扩充至满足为止。另外容量的
    扩充经历的过程是:重新分配空间,移动元素,释放原空间等动作。

    SGI STL list是一个环状双向链表,所以只需要一个指针,便可以完整表现整个链表。

    vector单向开口的连续线性空间,deque双向开口的连续线性空间。deque和vector的最大差异在于,一是允许以常数时间在首尾端插入或移除操作,二在于deque没有所谓容量的概念,因为它是动态地以分段连续空间组合而成,随时可以增加一段新的空间并链接起来。

    deque采用一小块连续空间(称为map)作为主控,其中的每一个元素是一个指针,指向另一段连续线性空间(缓存区),SGI STL deque可以指定缓冲区的大小,默认值0表示512字节。当map已经满载时,会重新分配较大的一块区域,拷贝原内容到新map。

    stack容器适配器,默认使用的容器是deque,也可以是其它满足特定要求(见参考手册)的容器。

    queue容器适配器,默认使用的容器是deque,同样可使用其它满足特定要求(见参考手册)的容器。

    priority_queue同样是一个容器适配器,默认使用容器是vector,一般内部是以最大二叉堆实现的(binary max heap)。binary heap是一种完全二叉树(complete binary tree)。

    slist,单向列表。C++11中添加了一个forward_list,支持在容器的任何位置快速的插入和删除元素,但不支持快速随机访问,另外相对于list来说,更加节省内存空间。

    关联式容器set、map、multiset与multimap底层一般都是使用RB-tree(一种平衡二叉树)实现的。hash_set, hash_map, hash_multiset, hash_multimap一般是使用hash table实现的。现在C++11提供了哈希表实现的容器unordered_set, unordered_multiset, unordered_map, unordered_multimap。

    哈希表
    负载系数:元素个数除以表格个数。
    解决碰撞问题的方法有:linear probing(解决碰撞问题的函数是一个线性函数),quadratic probing(解决碰撞问题的函数是一个二次函数),separate chaining(每个表格元素维护一个链表)等。

    SGI STL的hash table采用的是separate chaining method。

    注意:RB-tree有自动排序功能,而hash-table没有,这也体现到分别以她们为底层实现的容器的性质。

    stl_config.h

    STL配置参数

    stl_alloc.h (<memory>)

    // 一级内存分配器,当所需要内存大于128字节时调用该分配器,该类中的函数都是静态成员函数            
    template<int __inst>   
    class __malloc_alloc_template;
    
    内存分配和释放调用的是C标准库`malloc``realloc``free`,当使用`malloc``realloc`内存分配失败时(一般是内存不足),会调用用户自己设置的内存处理函数`__malloc_alloc_oom_handler`释放内存,已得到足够的空闲内存区域,然后重新使用`malloc`, `realloc`重新尝试申请分配内存。如果用户没有设置内存处理函数,那么直接抛出错误。其中,`__malloc_alloc_oom_handler`是通过成员函数`  static void (* __set_malloc_handler(void (*__f)()))()`设置的,该函数返回以前的内存处理函数。      
    
    // 内存分配类适配器       
    template<class _Tp, class _Alloc>
    class simple_alloc;
    
    typedef __malloc_alloc_template<0> malloc_alloc;
    typedef malloc_alloc alloc;
    typedef malloc_alloc single_client_alloc;
    
    // 内存分配适配器, 该适配器会多分配一段内存区域用于保存所分配的内存大小n,因此要保证该段内存区域足够大,以能够存储(size_t)n,一般保证大于sizeof(size_t)即可。n会用于reallocate与deallocate校验内存大小     
    template <class _Alloc>
    class debug_alloc;        
    
    debug_alloc有一个私有数据成员enum {_S_extra = 8},来用于多分配一段_S_extra大小的内存区域,该内存区域是在所分配的总内存区域的首部。其中,n(类型size_t)是存在该段内存区域中的。
    // 二级内存分配器,当分配的内存区域小于128字节时,会使用该内存分配器。使用16个链表维护一组内存区域,对应链表维护的内存大小对应为8,16,...,128。另外,该内是线程安全的。没分配一个对应大小的内存,就插入相应的链表中。      
    enum {_ALIGN = 8};       
    enum {_MAX_BYTES = 128};          
    enum {_NFREELISTS = 16}; // _MAX_BYTES/_ALIGN          
    
    // threads用于指定是否有多余一个线程在使用该分配器     
    template <bool threads, int inst>
    class __default_alloc_template;  
    typedef __default_alloc_template<__NODE_ALLOCATOR_THREADS, 0> alloc;
    typedef __default_alloc_template<false, 0> single_client_alloc;
    
    二级内存分配器的工作流程如下:
    该分配器维护一个大小为16的自由链表_S_free_list,每个链表的结点所指向的内存大小
    分别为816,...,128。当allocate分配内存时,如果所要分配的内存大小大于_MAX_BYTES(128字节),则直接调用一级内存分配器分配内存。
    否则,从_S_free_list相应的链表中取出一个结点(有足够内存大小的结点)作为所分配的内存,更新该链表;
    如果,相应的链表中没有内存,那么调用_S_refill申请一块大小__n的内存,该函数的解释见如下。
    当deallocate释放内存时,如果__n>_MAX_BYTES,则直接调用一级内存分配器释放内存。
    否则,将要释放的内存区域插入到相应的链表中。reallocate同理,是以realloc、allocate、memcpy、deallocate实现的。
    注意,allocate,deallocate,reallocate都是线程安全的。
    
    
    // 比较重要的私有成员函数解释如下     
    static void* _S_refill(size_t __n);      
    该函数尝试申请大小__n的内存区域,该函数调用了_S_chunk_alloc函数尝试申请
    __n*20大小的内存块,如果申请的内存大小刚好是__n(__nobjs等于1),则直接返回该内存地址;
    如果申请的内存大小大于__n,则将剩余余的内存(大小是__n的倍数,即__n-1个链表节点),插入到对应的链表中。
    
    static void* _S_chunk_alloc(size_t __size, int& __nobjs);      
    该函数尝试申请大小为__size*nobjs大小的连续内存区域,nobjs的默认大小是20。当     
    __S_start_free与__S_end_free之间的空间足够分配__size*20大小的内存空间时,
    则从这个空间中取出__size*20大小的空间,同时更新__S_start_free;当申请的空间
    大小不满足上一种情况,但是已有的内存空间大小大于__size时,则取出
    ((__S_start_free - __S_end_free)/(__size))* __size大小的内存空间,同时更新__nobjs与__S_start_free;当不满足以上两种情况时,如果还有剩余的内存空间(小于__size),
    则将这块内存插入相应的链表中,然后调用malloc分配2*__size*__nobjs+(_S_heap_size>>4)大小的内存空间。如果分配成功,则更新_S_start_free、_S_start_end、_S_heap_size,
    再次尝试从已分配空间上申请__size*__nobjs大小的内存空间,知道成功为止。如果分配不成功,则从_S_free_list中查找可用的空闲空间,再次尝试申请__size*nobjs大小的内存空间,直到成功为止。
    
    
    // 比较重要的私有数据成员如下       
    // 链表中的结点
    union _Obj {
        union _Obj* _M_free_list_link;
        char _M_client_data[1];    /* The client sees this.        */
    };
    // 16个自由链表,_NFREELISTS=16
    static _Obj* __STL_VOLATILE _S_free_list[_NFREELISTS]; 
    static char* _S_start_free; // 指向已分配空闲内存的首部      
    static char* _S_end_free;   // 指向已分配空闲内存的尾部      
    static size_t _S_heap_size; // 已分配内存的总大小          
    // C++标准中要求标准库实现的默认分配器           
    template<class Tp>      
    class allocator;      
    该类使用__default_alloc_template<__NODE_ALLOCATOR_THREADS, 0>进行内存分配
    
    template<>
    class allocator<void>;
    
    // SGI风格的分配器适配器,大概类似于C++11的use_allocator,不过接口不同
    template <class _Tp, class _Alloc>
    struct __allocator;        
    
    template <class _Alloc>
    class __allocator<void, _Alloc>;
    
    template <class _Tp, class _Allocator>
    struct _Alloc_traits

    stl_construct.h

    内部使用头文件,SGI非标准扩展,保持向后兼容性。
    construct, destroy在指定内存上构造对象(使用placement new实现),析构对象。

    stl_relops.h

    该文件中定义了几个模板函数,用于两个值之间的关系比较操作。所定义的模板函数分别是:
    operator!=, operator>, operator<=, operator>=,这些函数是以相等与小于比较操作实现的。

    stl_pair.h (<utility>)

    // 二元组的实现,数据成员first(type _T1), second(type _T2)       
    template<class _T1, class _T2>
    struct pair; 

    注意operator==operator<的实现,其它的!= > <= >=都是以== <实现的。判断二元组x==y
    是判断__x.first == __y.first && __x.second == __y.second,判断二元组x<y,是判断
    __x.first < __y.first || (!(__y.first < __x.first) && __x.second < __y.second)

    函数make_pair创建一个pair对象。

    stl_algobase.h (<algorithm>)

    iter_swap:交换两个迭代器所指向元素的值。
    swap:交换两个对象的值。
    min: 返回两个对象中较小的那一个,可指定比较函数对象(functors)。
    max:返回两个对象中较大的那一个,可指定比较函数对象
    copy:拷贝某个范围内的元素到另一个范围内,返回最后一个被拷贝的元素的迭代器。当所拷贝的对象是trivial的,那么使用memmove实现对象的拷贝。否则,逐个元素赋值。用户自己保证新的范围足够大,以满足拷贝。
    copy_backward:逆序拷贝某个范围内的元素到另一个范围内,返回最后一个被拷贝的元素的迭代器。当所拷贝的对象时trivial的,那么使用memmove实现对象的拷贝。否则,逐个元素赋值。用户自己保证新的范围足够大,以满足拷贝。
    copy_n:拷贝n个元素到新的位置。当拷贝源是随机迭代器时,内部实现调用了copy函数。否则,逐个元素赋值。注意SGI的版本与C++11的语义有些不同,返回值不同。
    fill:将某个范围内的元素都赋值为某个值。当被赋值元素的类型是单字节类型时,使用memset。
    fill_n:将某个值赋值给n个元素,返回最后一个被赋值元素的迭代器。当被赋值元素的类型是单字节类型时,使用memset。
    mismatch:返回两个范围内第一个不同元素的位置,可以指定比较函数。
    equal:判断两个集合的元素是否都相同,可以指定比较函数。
    lexicographical_compare:按词典顺序比较两个有序集合,可指定比较函数。如果两个集合的元素是单字节形式,则调用memset进行比较。什么是词典顺序:按序比较两个集合的元素,当遇到第一个集合的某个元素小于第二集合同一对应位置的元素时,返回true;大于时,返回false;两个集合按序比较完全相同时,返回false;两个集合按序比较完全相同时,但第一个集合的元素个数小于第二个集合,则返回true。两个集合按序比较完全相同时,但第一个集合的元素个数大于第二个集合,则返回false。
    lexicographical_compare_3way:非C++标准中的函数。按词序比较,返回-1(小于), 0(完全相同时), 1(大于)。

    stl_uninitialized.h (<memory>)

    uninitialized_copy:拷贝某个范围的对象到一个未初始化的内存区域。当元素类型是POD类型时,调用copy函数拷贝。否则,逐个构造初始化。当元素类型是char或wchar_t类型时,调用memmove实现。
    uninitialized_copy_n:拷贝n个对象到一个未初始化的内存区域。当迭代器时输入迭代器时,逐个构造初始化。当迭代器时随机迭代器时,调用uninitialized_copy。
    uninitialized_fill:拷贝一个对象到一个未初始化的内存区域。当拷贝对象时POD类型时调用fill函数。否则,逐个构造初始化。
    uninitialized_fill_n:拷贝一个对象到一个未初始化的内存区域。当拷贝对象时POD类型时调用fill函数。否则,逐个构造初始化。

    stl_tempbuf.h (<memory>)

    get_temporary_buffer:获得一块未初始化的内存区域。内部实现使用malloc,当初始申请分配的空间大小大于可以分配的最大内存空间大小INT_MAX/sizeof(_Tp)时,从_len=INT_MAX/sizeof(_Tp)大小开始尝试分配内存空间。当内存空间不足分配指定大小__len时,减半__len/2再次尝试分配空间,直到内存分配成功为止。如果一直分配不成功,返回pair<_Tp*, ptrdiff_t>((_Tp*)0, 0)
    return_temporary_buffer:释放所分配的未初始化的内存空间,使用free释放内存空间。
    此外,还有一个非标准的temporary_buffer类实现。

    stl_heap.h (<algorithm>)

    关于堆,参考《算法导论》第6章堆排序。
    push_heap: 该函数将__last-1位置的元素插入到[__first, __last-1)定义的最大堆上。第一个版本使用operator<比较元素的大小,第二个版本使用函数对象__comp比较元素的大小。
    pop_heap: 该函数交换__first__last-1的元素,同时维护[__first, __last-1)最大堆的性质,等价于从最大堆中弹出最大的元素。
    make_heap:在[__first, __last)上构造一个最大堆。
    sort_heap:将一个最大堆[__first, __last)按升序排序,可指定比较函数对象,默认是使用operator<

    关键函数解释:

    // 该函数用来维护堆的性质,当堆上某个结点的元素改变时,在以该结点为根的子堆上逐层恢复最大堆的性质
    template <class _RandomAccessIterator, class _Distance, class _Tp>
    void __adjust_heap(_RandomAccessIterator __first, _Distance __holeIndex,
                  _Distance __len, _Tp __value)
    {
      _Distance __topIndex = __holeIndex;  // 子堆根节点
      _Distance __secondChild = 2 * __holeIndex + 2;    // 根节点右孩子
      while (__secondChild < __len) {
        if (*(__first + __secondChild) < *(__first + (__secondChild - 1)))
          __secondChild--;
        *(__first + __holeIndex) = *(__first + __secondChild);
        __holeIndex = __secondChild;  // 将该结点作为根节点
        __secondChild = 2 * (__secondChild + 1);
      }
      if (__secondChild == __len) {
        *(__first + __holeIndex) = *(__first + (__secondChild - 1));
        __holeIndex = __secondChild - 1;
      }
        // 向[__first, __last)最后一个元素插入到[__frist, __last-1)的最大堆中
      __push_heap(__first, __holeIndex, __topIndex, __value);
    }
    
    // 该函数生成最大堆,利用了最大堆的性质:最大堆的n/2+1 .. n的结点为叶节点
    template <class _RandomAccessIterator, class _Tp, class _Distance>
    void __make_heap(_RandomAccessIterator __first,
                _RandomAccessIterator __last, _Tp*, _Distance*)
    {
      if (__last - __first < 2) return;
      _Distance __len = __last - __first;
      _Distance __parent = (__len - 2)/2;
    
      while (true) {
        __adjust_heap(__first, __parent, __len, _Tp(*(__first + __parent)));
        if (__parent == 0) return;
        __parent--;
      }
    }

    stl_numeric.h (<numeric>)

    accumulate:给定初始值__init,以及某个范围的元素[__first, __last)计算它们的累加和。可以指定累加计算函数对象,默认是使用operator+
    inner_product:给定初始值__init,以及两个范围的元素集[__first1, __last)[__first2, ...),计算它们的内积。可以指定元素之间相乘的计算函数以及累加计算函数,默认是使用operator*operator+
    partial_sum:计算给定范围[__first, __last)的子范围的部分和,可以指定部分和计算函数,默认是使用operator+
    adjacent_difference:计算给定范围[__first, __last)的毗邻元素的差,可以指定差计算函数,默认是使用operator-
    power:非标准函数。计算__x ** n,可以指定相乘计算函数,默认是使用函数对象std::multiplies
    itoa:C++11开始支持的标准函数。以__value为初始值,按序递增填充[__first, __last)

    stl_algo.h (<algorithm>)

    for_each:将指定函数应用到[__first, __last)的每一个元素。
    find:在指定范围寻找和指定的值相等的元素。如果找到返回指向该值的迭代器,没有找到返回__last
    find_if:在指定范围寻找和指定的值相等(指定一个函数对象用来判断元素是否相等)的元素。如果找到返回指向该值的迭代器,没有找到返回__last
    adjacent_find:在指定范围内寻找毗邻两个元素相等的位置,没有找到返回__last,可以指定比较函数,默认是使用operator ==
    count:寻找指定范围内和某个值相等的元素的个数,没有找到返回__last
    count_if:寻找指定范围内满足某种规则的元素的个数。
    search:在指定范围[__first1, __last1)寻找指定子范围[__first2, __last2),如果没有找到范围返回__last1。可以指定比较函数判断两个元素是否相等,默认使用operator==。算法大致描述,考虑指定范围与子范围为空的情况,子范围为单个元素的情况,以及子范围非单个元素的情况。内部使用find在[__first1, __last1)中找到子范围的第一个元素的位置,然后判断后续的元素是否相等,在[__frist1, __last1)中找到子范围或找完[__first1, __last1)所有元素为止。
    search_n:在指定范围[__first, __last)上寻找连续的__count__value值,算法和search类似。可以指定二元函数用于比较元素的相等,默认是使用operator==
    swap_ranges:交换两个指定范围内的元素的值,内部调用iter_wap来交换两个指定位置的值。
    transform:将指定的一元函数,应用到某个指定范围的每个元素上;将指定的二元函数,应用到某两个指定范围的元素上。返回最后一个变换结果的迭代器。

    replace:将指定某个范围内的某个所有的旧值全都替换为某个新值。
    replace_if:将指定某个范围内满足某种规则的所有旧值全都替换为某个新值。
    replace_copy:将某个范围的元素全都拷贝到另一个新范围,并用某个新值替换掉新范围中所有的某个旧值。
    replace_copy_if:将某个范围的元素全都拷贝到另一个新范围,并用某个新值替换掉所有满足某种规则的旧值。
    generate:将某个函数的结果保存到一个范围中。
    generate_n:将某个函数的结果保存到一个范围中,和generate实质上只是参数不同而已。
    remove_copy:将[__first, __last)中除了值__value之外的其它元素都拷贝一个新的范围中,返回新范围最后一个被拷贝元素的迭代器。
    remove_copy_if:将[__first, __last)中不满足某种规则的元素都拷贝到一个新的范围中, 返回新范围最后一个被拷贝元素的迭代器。
    remove:将[__first, __last)中所有的__value移除,内部实现调用find找到范围中的第一个__value的位置,然后调用remove_copy实现移除后续的所有__value元素。
    remove_if:将[__first, __last)中满足某种规则的所有元素移除,以find_if和remove_copy_if实现。
    unique_copy:将[__first, __last)中非连续相等的元素拷贝到一个新的区域中。可以指定二元函数,用来判断两个元素是否相等。
    unique:移除[__first, __last)中连续相等的元素(保留第一个),可以指定二元函数,用来判断两个元素是否相等。以adjacent_find和unique_copy实现。
    reverse:逆序[__first, __last)中的元素。
    reverse_copy:逆序拷贝[__first, __last)到一个新的区域。
    rotate:以轴__middle旋转[__first, __last)[__fist, ..., __middle, ..., __last) -> [__middle, ..., __last-1, __first, .. -middle)
    rotate_copy:以轴__middle旋转[__first, __last),拷贝到一个新的位置。
    random_shuffle:随机重排序[__first, __last),可指定随机数生成器,默认使用rand或lrand48(如果支持)。
    random_sample_n:C++ TS支持。随机从[__first, __last)采样n个元素,可指定随机数生成器,默认使用rand或lrand48(如果支持)。
    random_sample:C++ TS支持,随机从[__first, __last)采样__out_first - __out_last个元素,可指定随机数生成器,默认使用rand或lrand48(如果支持)。
    partition:将[__first, __last)按照某种规则分为两组,返回第二组首个元素的迭代器。有两个版本,前向迭代器每个组中的元素的相对顺序时不变的,双向迭代器的实现版本没有这一性质,线性复杂度。
    stable_partition:将[__first, __last)按照某种规则分为两组,保留元素之间的相对顺序。内部有两种实现,一种是分治策略递归实现,另一种是申请分配一段临时内存,将其某一组拷贝到该临时内存中,然后在组合在一起。有足够内存线性复杂度,否则复杂度为(last-first)*log(last-first)。
    sort:按升序对[__first, __last)排序,不保证相等元素之间的相对顺序,使用归并排序实现。算法复杂度N*logN。
    stable_sort:按升序对[__first, __last)排序,保证相等元素之间的相对顺序,归并排序实现。有足够内存,算法复杂度N*logN,否则N*log^2(N)。
    partial_sort:排序前n个元素,利用堆排序实现,算法复杂度(last-first)log(middle-first),可指定比较函数。
    partial_sort_copy:排序某个范围内(L=min(first-last, out_first-out_last)的元素,并将其拷贝到另一个新范围,堆排序实现。算法复杂度Nlog(L)。 nth_element:仅排序第n个元素。
    lower_bound:在[__first, __last)中(已排序)寻找第一个不小于给定值的元素,二分查找法实现。可以指定比较函数。
    upper_bound:在[__first, __last)中(已排序)寻找第一个大于给定值的元素,二分查找法实现。可以指定比较函数。
    equal_range:在[__first, __last)中(已排序)寻找和给定值相等的元素范围,二分查找法实现。可以指定比较函数。
    binary_search:判断给定值是否出现已排序的序列中,调用lower_bound实现。可以指定比较函数。
    merge:合并两个序列为一个已排序的序列,可以指定比较函数,默认使用operator<
    inplace_merge:合并两个连续的序列为一个已排序的序列,相等元素的原始顺序保持不变,可以指定排序函数。当有足够的内存大小时,算法复杂度为N,否则为N*logN。
    includes:判断[__first2, __last2)是否为[__first1, __last1)的子集,算法要求两个序列都是已排序的。可以指定比较函数。
    set_union:取两个已排序序列的并集,所取得的并集也是已排序的。可以指定比较函数。
    set_intersection:取两个已排序序列的交集,所取得的交集也是已排序的。可以指定比较函数。
    set_difference:取两个集合的差集,将[__first1, __last1)(已排序)中未在[__first2, __last2)(已排序)的元素到一个新的序列,结果亦是已排序的。可以指定比较函数。
    set_symmetric_difference:取两个有序集合之间的差集(两个集合之间不相同的元素),结果也是有序的。可以指定比较函数。
    min_element:查找[__first, __last)中最小的元素。可以指定比较函数。
    max_element:查找[__first, __last)中最大的元素。可以指定比较函数。
    next_permutation:求一个排列组合的下一个排列组合。可以指定比较函数。
    prev_permutation:求一个排列组合的上一个排列组合。可以指定比较函数。
    find_first_of:在[__fist1, __last1)中查找与[__first2, __last2)中某个元素相等的第一个元素。可以指定比较函数。
    find_end:在序列[__fist1, __last1)中查找最后一个出现的子序列[__first2, __last2),,没有找到返回__last1。可以指定比较函数。
    is_heap:C++11中开始支持。判断给定序列是否是一个最大堆。可以指定比较函数。
    is_sorted:C++11中开始在支持。判断给定序列是否以升序排序。可以指定比较函数。

    stl_iterator_base.h (<iterator>)

    // 用于标识迭代器类型
    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 {};
    // 迭代器基类,C++所有迭代器都有iterator_category, value_type, difference_type, pointer与reference这五种可traits的类型。
    template <class _Category, class _Tp, class _Distance = ptrdiff_t,
              class _Pointer = _Tp*, class _Reference = _Tp&>
    struct iterator { 
      typedef _Category  iterator_category;
      typedef _Tp        value_type;
      typedef _Distance  difference_type;
      typedef _Pointer   pointer;
      typedef _Reference reference;
    };
    
    // iterator_traits用于萃取迭代器的相关类型信息     
    // 注意指针与常量指正也是一种迭代器     
    template<class _Iterator>   
    strcut iterator_traits; 

    stl_iterator.h (<iterator>)

    // 迭代器适配器,用于在容器尾部插入,所以它的迭代器标识是output_iterator_tag
    template <class _Container>
    class back_insert_iterator;
    
    // 迭代器适配器,用于在容器首部插入,其它同理back_insert_iterator
    template <class _Container>
    class front_insert_iterator;
    
    // 迭代器适配器,用于在容器中插入,插入位置由指定的容器的迭代器提供,见其构造函数
    template <class _Container>
    class insert_iterator;
    
    insert_iterator(_Container& __x, typename _Container::iterator __i) : container(&__x), iter(__i) {}
    // 迭代器适配器,用于反转双向迭代器,其迭代器标识是bidirection_iterator_tag
    template <class _Iterator>
    class reverse_iterator;
    // 输入迭代器,从std::basic_istream<_CharT, _Traits>读,迭代器标识是input_iterator_tag
    template <class _Tp, 
              class _CharT = char, class _Traits = char_traits<_CharT>,
              class _Dist = ptrdiff_t> 
    class istream_iterator;
    
    // 注意该类的三个私有数据成员
    basic_istream<_CharT, _Traits>* _M_stream;  // 表示输入流      
    _Tp _M_value;   // 从输入流中读入的_Tp类型的数据      
    bool _M_ok;     //表示流是否读完
    
    
    // 偏特化版本。其中,std::istream作为输入流     
    template<class _Tp, class _Dist>     
    class istream_iterator;
    // 输出迭代器,写到std::basic_ostream<_CharT, _Traits>,迭代器标识是output_iterator_tag
    template <class _Tp, class _CharT = char, class _Traits = char_traits<_CharT>
    class ostream_iterator;     
    // 注意该类的三个私有数据成员 
    basic_ostream<_CharT, _Traits>* _M_stream;
    const _CharT* _M_string;    // 用作输出分隔符
    
    // 偏特化版本。其中,std::ostream作为输出流    
    template<class _Tp>     
    class ostream_iterator;         
    // 输入流缓存迭代器,从std::basic_streambuf读,迭代器标识是input_iterator_tag。
    // ++与*操作内部实现调用了basic_streambuf的snextc与sgetc成员函数
    // 另外需要注意的是equal, operator==, operator!=都是判断两个流缓存迭代器是否都是有效的或都不是有效的,而不管它们所使用的streambuf对象是否相同。        
    template<class _CharT, class _Traits>
    class istreambuf_iterator
      : public iterator<input_iterator_tag, _CharT,
                        typename _Traits::off_type, _CharT*, _CharT&>;        
    
    // 私有数据成员   
    basic_streambuf<_CharT, _Traits>* _M_buf;  // 表示缓存输入流缓存    
    mutable _CharT _M_c; // 从流中读入的上一个或当前的字符       
    mutable bool _M_eof : 1; // 判断缓存是否读完或为空     
    mutable bool _M_is_initialized : 1; // 判断是否已经对缓存操作过(调用过operator*或operator++)           
    // 在C++中,mutable也是为了突破const的限制而设置的。被mutable修饰的变量,将永远处于可变的状态,即使在一个const函数中。
    // 输出流缓存迭代器
    template<class _CharT, class _Traits>
    class ostreambuf_iterator
      : public iterator<output_iterator_tag, void, void, void, void>
    
    // 私有数据成员
    basic_streambuf<_CharT, _Traits>* _M_buf;   // 指向streambuf     
    bool _M_ok;     // streambuf是否可用

    参考资料

    1. sgi STL
    2. cppreference.com
    3. cplusplus.com
    4. CSDN KangRoger的专栏
    5. 算法导论 Thomas H.Cormen / Charles E.Leiserson / Ronald L.Rivest / Clifford Stein
    6. STL源码剖析
  • 相关阅读:
    EntityFramework.Extended 支持 MySql
    向着那个理想的世界奔跑
    DDD 领域驱动设计-两个实体的碰撞火花
    云自无心水自闲
    JQuery 复制粘贴上传图片插件(textarea 和 tinyMCE)
    理解 .NET Platform Standard
    【补充】Gitlab 部署 CI 持续集成
    DDD 领域驱动设计-领域模型中的用户设计
    CSS float 定位和缩放问题
    JQuery 加载 CSS、JS 文件
  • 原文地址:https://www.cnblogs.com/corfox/p/6063308.html
Copyright © 2011-2022 走看看