本文主要介绍STL中的基本算法,主要涉及到的源码文件有 stl_algobase.h
等。
在 stl_algobase.h
中定义的算法都比较简单基础,主要涉及区间相等判断、区间填充、求极值、交换、拷贝、字典序比较等算法,而其他诸如查找、计数、排序、旋转等算法则在文件 stl_algo.h
中实现。在algobase基本算法中,除了字典序比较、复制/拷贝算法外,其他都比较简单,这里先依次介绍这些简单的算法,然后再介绍字典序比较和拷贝算法。
1. 交换、填充等简单算法
由于这里很多算法比较简单(基本都在10行以内,甚至很多就一行代码),就不一一粘贴代码了。
iter_swap :将两个 ForwardIterators 所指的对象对调,通过申请一个临时变量、三次赋值,就完成了。
min/max :求两个数中的小、大者,还有一个版本可以指定的比较方法(仿函数)。
fill :将 [first, last)
内的所有元素改填为新值 value。
fill_n :将 [first, last)
内的前n个元素改填为新值 value,返回迭代器指向被填入的最后一个元素的下一位置。
mismatch :用来平行比较两个序列,指出两者之间的第一个不匹配点,返回一对迭代器(Iterators Pair),分别指向两序列中的不匹配点。
equal :判断两个序列在 [first, last)
区间内相等,如果第二个序列元素较多,将不予考虑,只有两个序列在各自区间内对应相等才返回true,否则返回false。
2. 字典序比较
lexicographical_compare
以“字典序排列方式”对两个序列 [first, last)
和 [first2, last2)
进行比较。比较操作针对两个序列中的对应位置上的元素进行,直到某一对不相等或同时到达尾部或仁义序列到达尾部。该算法其实并不复杂,但有一点值得注意,那就当且仅当第一个序列字典序小于第二个序列时才返回true,以下是各种情况下的返回值:
- 发现不相等,如果第一序列元素较小,返回true,否则返回false;
- 到达last1而尚未到达last2,返回true;
- 到达last2而尚未到达last1,返回false;
- 同时到达last1和last2,返回false。
源码如下:
1 2 3 4 5 6 7 8 9 10 |
|
除了这个默认的版本外,还有一个版本提供比较方法(仿函数)的参数。另外,对于纯字符串的比较,SGI STL还做了进一步优化,使用原生指针和C标准函数 memcmp()
进行比较,如下:
1 2 3 4 5 6 7 8 |
|
3. 复制/拷贝算法
在很多应用程序中,复制copy是一个很常见的操作,特别是在赋值的时候。对于稍微复杂的对象,在不同的语言中赋值时会有一些差别,有的编程语言赋值仅仅是对等号右边的对象的一个引用,而并没有正真的产生一个新的对象,更不用说对象中可能包含的对象成员,例如Python当中的赋值、浅拷贝copy和深拷贝deepcopy等。
而STL 中的copy,除了简单的单一对象的拷贝之外,还有序列区间的拷贝等,这里就涉及到空间分配和时间效率问题。在C++中,复制操作主要是运用assignment operator(复制运算符) 或 copy constructor(拷贝构造函数),在STL的copy算法中使用的是前者,而对于某些具有trivial assignment operator的数据,则可以使用内存直接复制行为(例如C标准库函数memmove、memcpy等),就能极大的节省时间。SGI STL用尽各种办法,包括函数重载、型别特性、偏特化(partial specialization)等技巧(关于偏特化请参见 C++模板特化与偏特化),无所不用其极地加强效率。
除了上面提到的元素型别、偏特化等问题,还有元素复制顺序的问题。copy 算法是将原始区间 [first, last)
内的元素复制到目标区间 [result, result+last-first)
区间内,复制时既可以从 first 开始往 last 复制,但也可以从 last-1开始向 first 复制,后者在 STL 另取名为 copy_backward_。从后往前复制的好处在于,不用担心目标区间与原始区间有重叠,因为如果有重叠区域,那么简单的 copy 时,对于原始数据而言 [result, last)
区间的数据在被复制前被修改了,从而得不到预期的结果。当然,有一种情况使用 copy 不用担心这个问题,那就是对于迭代器为原生指针,使用 memmove (而不是 memcpy,关于二者的区别参见 memcpy() vs memmove())进行复制,此时 memmove 会先将整个区间复制下来,没有被覆盖的危险。
在介绍 copy 算法的源码具体实现前,根据源码及其注释再做一个简单的小结:copy 算法中的一些辅助函数有两个目的,其一是对于简单的数据类型尽量使用 memmove,其二是对于具有 RandomAccessIterators 的对象使用一个计数器来进行循环;除此之外,SGI STL针对编译器是否具有函数模板偏特化、类模板偏特化等进行了适配。下面是 copy 的源码,其中添加了比较详细具体的注释:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 |
|
以上是 copy 的完整代码,关于复制还有两个接口,一个是 copy_n
,另一个是 copy_backward
,前者复制区间 [first, last)
中前 n 个元素,后者从last-1 往 first 复制,这里就不详细展开了。