–本文章来自侯捷老师的STL课程和自己的理解
若有错误,欢迎指正
现在要说到STL里面另一大重要部件了,算法。
其实在我个人的理解里面,整个STL都是为了能让元素,对象更好的使用算法而建立的。我觉得如果把握住了为算法服务的这个思想,很多东西其实就都能理解了。
还记得迭代器的五种种类吗?因为算法对容器内部的元素一无所知,所以所有的信息都需要由迭代器来提供。
那么为什么要设计这么多的迭代器种类呢?
我们打开部分算法的源码可以发现,原来迭代器种类的不同也会影响算法效率:
上方的算法是distance(),用于计算两个迭代器之间的距离。换句话来说,也就是计算容器里面,迭代器所指向的两个元素中间还有多少元素。我们可以看到这个函数在调用时有两个不同的版本。
一个是随机迭代器版本
一个是双向迭代器版本
一个是只读迭代器版本
随机迭代器版本可以直接通过两个迭代器重载的"-"得到距离,而只读迭代器的版本却只能通过++来逐个运算。这在运算效率上的差别是非常夸张的。
假如中间距离隔了一百万个元素。随机迭代器很大可能在O(1)就可以得出距离,但是只读迭代器的版本却可能需要一百万个计算单位时长,但是双向迭代器两个迭代器同时运算又可能让这个时长减半。
所以,迭代器对算法的最大影响在于不同的迭代器有不同的效率。指出迭代器的种类可以一般都可以获得最优效率的版本。
这里需要指出,假如输入的迭代器种类没有对应的版本,一般都会使用其父类版本。
同样还是拿上方的函数来举例子,如果输入的是单向迭代器,则依旧使用只读迭代器的版本。
同理,针对输入的type不同(这里指type_traits输出的那五种信息),也会导致底层代码的处理方式不一样。同样,也是借由特化来实现的,如下图:
关于迭代器,之前介绍过还有一种反向迭代器,其内部实现就是通过将所有的操作取反来得到和原本算法加上正向迭代器的反向版本。
比如sort()函数可能得到的是升序排列的元素。
但是使用反向迭代器,就可以得到降序排列的元素。
这也是迭代器对算法的影响之一。这里涉及到一点点适配器的概念了。
最后,再贴出课件里面常用的函数源码:
然后是仿函数。
为什么把他放到和函数一起讲呢,是因为我学完之后觉得仿函数其实也就是算法函数的一种,是为算法服务的。一般都是通过重载操作符"()"来实现的。
上图的这几个仿函数分别完成了不同的基本功能,要么是比较大小,要么是求值。这些都是可以传入给算法的功能性函数。比如传给sort()函数less改变其元素的升降排列顺序。
但是都有一个共同点,都继承自binary_function。
并且,是否继承自这个类,决定了这个仿函数有没有融入进STL的体系结构。
为什么呢?
这里和迭代器的Type_traits有一点点相同的地方。都是通过typedef定义出了这几个信息:
我们需要仿函数必须提供上图中的两个或者三个信息(也就是左边的两个模板类),只有提供了这几个信息,在能够支持算法工作的同时,还能够支持适配器的工作。
关于适配器的内容,可以参见STL系列(10)。