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源码剖析
posted @ 2016-07-31 15:44  corfox  阅读(446)  评论(0编辑  收藏  举报