STL源码剖析(三)

算法

从语言的角度看:ios

  • 容器 Container 是一个class template
  • 算法 Algorithm 是一个function template
  • 迭代器 Iterator 是一个class template
  • 函数式 Functor 是一个class template
  • 适配器 Adapter 是一个class template
  • 分配器 Allocator 是一个class template

Algorithm 和 Container 之间没有直接的联系,Algorithm 没法得知 Container 都有什么信息,因此须要经过 Iterator 来获取内部的信息,因此 Iterator 就必需要与 Algorithm 之间有这必定的交接规则,这样 Iterator 才能适配 Algorithm 的操做。算法

Algorithm的大概形式以下:dom

template<typename Iterator>
Algorithm(Iterator itr1, Iterator itr2)
{
    ...
}

template<typename Iterator, typename Cmp>
Algorithm(Iterator itr1, Ilerator itr2, Cmp comp)
{
    ...
}

迭代器的分类

各类容器的 iterators 有5种 iterator_category函数

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{}

Input 迭代器只能向前移动,一次一步,客户只有可读取(不能涂写)他们所指的东西,并且只能读取一次。它们模仿指向输入文件的阅读指针;C++ 程序库中的istream_iterator是这一分类的表明。指针

Ouput 迭代器状况相似,但一切只为输入:它们只能向前移动,一次一步,客户只可涂写它们所指的东西,并且只能涂写一次。它们模仿指向输出文件的涂写指针;ostream_iterator是这一分类的表明。code

Forward 迭代器,这种迭代器能够作前两种迭代器的每一件事,并且能够读或写其所指物一次以上。这使得他们可施行于屡次性操做算法。STL并未提供单向linked list,但某些程序库有(slist),而这种容器的迭代器就属于 forward 迭代器。继承

Bidirectional 迭代器,除了能够向前移动,还能够向后移动。STL的list迭代器就属于这一分类,set, multiset,map 和 multimap 的迭代器也都是这一分类。input

random_access 迭代器能够在常量时间内向前或者向后跳跃任意距离。这样的算术很相似指针算术。源码

迭代分类对算法的影响

首先是一个distance迭代器之间距离的算法,distance算法经过对迭代器分类的判断分别调用不一样的实现函数。其中,由于迭代器的分类有继承关系,再根据函数匹配的原则,不一样分类的迭代器会自动选择适合的实现方法。it

template <class InputIterator>
inline iterator_traits<InputIterator>::difference_type
distance(InputIterator first, InputIterator last){
    typedef typename iterator_traits<InputIterator>::iterator_category category;
    return __distance(first, last, category());
}

template<class InputIterator>
inline iterator_traits<InputIterator>::difference_type __distance(InputIterator first, InputIterator last, input_iterator_tag){
    iterator_traits<InputIterator>::difference_type n =0;
    while(first != last){
        ++first;
        ++n;
    }
    return n;
}

template <class RandomAccessIterator>
inline iterator_traits<RandomAccessIterator>::difference_type __distance(RandomAccessIterator first, RandomAccessIterator last, random_access_iterator_tag){
    return last - first;
}

与distance相似的算法举例:

template<class InputIterator, class Distance>
inline void advance(InputIterator& i, Distance n){
    __advance(i, n, iterator_category(i));
}

//此方法与iterator_traits<InputIterator>::iterator_category一致
template <class Iterator>
inline typename iterator_traits<Iterator>::iterator_category iterator_category(const Iterator&){
    typedef typename iterator_traits<Iterator>::iterator_category category;
    return category();
}

template<class InputIterator, class Distance>
inline void __advance(InputIterator& i, Distance n, input_iterator_tag){
    while(n--) ++i;
}

template<class BidirectionalIterator, class Distance>
inline void __advance(BidirectionalIterator& i, Distance n, bidirectional_iterator_tag){
    if(n >= 0)
        while(n--) ++i;
    else
        while(n++) --i;
}

template<class RandomAccessIterator, class Distance>
inline void __advance(RandomAccessIterator& i, Distance n, random_accrss_iterator_tag){
    i += n;
}

算法没法强制要求传入指定类型的迭代器,由于算法是一种模板,因此理论上能够传入全部类型的参数。为了尽量保证算法正常工做,算法会“暗示”使用者出入怎样类型的迭代器。具体的实现是经过在 template<class RandomAccessIterator, class Distance> 这其中的 RandomAccessIterator 就是对传入类型的“暗示”。

部分算法源码剖析

accumulate

template <class InputIterator, 
          class T>
T accumulate(InputIterator first, 
             InputIterator last, 
             T init)
{
    for(;first != last; ++first)
        init = init + *first;//将元素累加至初值init身上
    return init;
}

template <class InputIterator, 
          class T,
          class BinaryOperation>
T accumulate(InputIterator first, 
             InputIterator last, 
             T init,
             BinaryOperation binary_op)
{
    for(;first != last; ++first)
        init = binary_op(init, *first);
    return init;
}

一般算法会有两个版本,一个版本适用默认的规则,另外一个版本可让用户传入一个自定义的“规则”。

accumulate 使用的例子:

#include <iostream>     //std::out
#include <functional>   //std::minus
#include <numeric>      //std::accumulate
int myfunc(int x, int y) { return x + 2 * y;}
//function object
struct myclass{
    int operator()(int x, int y) { return x + 3 * y;}
} myobj;

int main()
{
    int init = 100;
    int nums[] = {10, 20, 30};

    cout << "using default accumulate:";
    cout << accumulate(nuyms, nums+3, init); //160
    cout << "\n";

    cout << "using functional's minus:";
    cout << accumulate(nuyms, nums+3, init, minus<int>()); //40
    cout << "\n";

    cout << "using custom function:";
    cout << accumulate(nuyms, nums+3, init, mufunc); //220
    cout << "\n";

    cout << "using custom object:";
    cout << accumulate(nuyms, nums+3, init, myobj); //280
    cout << "\n";
}

for_each

针对容器中的每个元素都进行一次操做

template <class InputIterator,
          class Function>
Function for_each(InputIterator first,
                  InputIterator last,
                  Function f)
{
    for(; first != last; ++first)
        f(*first);
    return f;
}

replace, replace_if, replace_copy

replace:范围内全部等于old_value的元素都以new_value替换

template <class ForwardIterator,
          class T>
void replace(ForwardIterator first,
             ForwardIterator last,
             const T& old_value,
             const T& new_value){
    for(;first != last; ++first)
        if(*first == old_value)
            *first = new_value;
}

replace_if:范围内全部知足pred()的元素都以new_value替换

template <class ForwardIterator,
          class Predicate,
          class T>
void replace_if(ForwardIterator first,
             ForwardIterator last,
             Predicate pred,
             const T& new_value){
    for(;first != last; ++first)
        if(pred(*first))
            *first = new_value;
}

replace_copy:范围内全部等于old_value的元素都以new_value放入新的空间内

template <class ForwardIterator,
          class T>
void replace(ForwardIterator first,
             ForwardIterator last,
             OutputIterator result,
             const T& old_value,
             const T& new_value){
    for(;first != last; ++first, ++result)
        *result = *first == old_value ? new_value : *first;
    return result;
}

count, count_if

count:统计等于value的元素个数

template <class InputIterator, class T>
typename iterator_traits<InputIterator>::difference_type 
count(InputIterator first, 
      InputIterator last, 
      const T& value){
    typename iterator_traits<InputIterator>::difference_type n = 0;
    for(; first != last; ++first)
        if(*first == value)
            ++n;
    return n;
}

count_if:统计知足pred()value的元素个数

template <class InputIterator, class Predicate>
typename iterator_traits<InputIterator>::difference_type 
count_if(InputIterator first, 
         InputIterator last, 
         Predicate pred){
    typename iterator_traits<InputIterator>::difference_type n = 0;
    for(; first != last; ++first)
        if(pred(*first))
            ++n;
    return n;
}

以上显示的标准库中的算法,有些容器使用标准库的算法效果不是很好或者效率不够高,那么这些容器会在成员函数中加入同名的函数。不带成员函数count()的容器有:array, vector, list, forward_list, deque;带成员函数count()的容器有:set/multiset, map/multimap, unordered_set/unordered_multiset, unordered_map/unordered_multimap。经过分析发现自带成员函数的这些容器都是关联性容器,能够依据key快速查找到value,因此实现本身特有的函数速度会更快。

find, find_if

find:循环遍历查找

template <class InputIterator, class T>
InputIterator find(InputIterator first,
                   InputIterator last,
                   const T& value)
{
    while(first != last && *first != value)
        ++first;
    return first;
}

find_if:根据条件循环遍历查找

template <class InputIterator, class T>
InputIterator find(InputIterator first,
                   InputIterator last,
                   Predicate pred)
{
    while(first != last && !pred(*first))
        ++first;
    return first;
}

count()同样,关联性容器有本身特有的find()成员函数。

二分查找,前提是已是有序序列!

template <class ForwardIterator,
          class T>
bool binary_search(ForwardIterator first,
                   ForwardIterator last,
                   const T& val)
{
    first = std::lower_bound(first, last, val);
    return (first != last && !(val < *first>));
}

lower_bound: 在不影响缘由顺序的前提下,找到能够插入的第一个位置。例如序列{10, 10, 10, 20, 20, 20, 30, 30, 30},如今须要插入20,则lower_bound返回指向第一个20的位置。同理,upper_bound指向最后一个20的后面。

template <class ForwardIterator,
          class T>
ForwardIterator lower_bound(ForwardIterator first,
                            ForwardIterator last,
                            const T& val)
{
    ForwardIterator it;
    iterator_traits<ForwardIterator>::difference_type count, step;
    count = distance(first, last);
    while(count>0)
    {
        it = first;
        step = count/2;
        advance(it, step);
        if(*it < val)//或者能够是 if(comp (*it, val))
        {
            first = ++it;
            count -= step + 1;
        }
        else count = step;
    }
    return first;
}
相关文章
相关标签/搜索