各个容器的iterator的iterator_category

vector迭代器的iterator_category是random_access_iterator_tag,支持随机访问,deque和vector一样;

list迭代器的iterator_category是bidirectional_iterator_tag,支持双向移动,set、map也一样;

unordered_set和unordered_map底层是hash table,iterator_category是farward_iterator_tag,支持单向移动。

算法需要知道各个容器的iterator的iterator_category,以便算法选择一条最优的方法。

acculate算法分析

acculate算法使用:

class myfunc1{
public:
    int operator()(int a,int b){
        return a +2* b;
    }
};
int myfunc2(int a, int b)
{
    return a + 3 * b;
}
void test02()
{
    vector<int>v;
    for (size_t i = 0; i < 10; i++)
    {
        v.push_back(i);
    }
    cout << accumulate(v.begin(), v.end(), 100, myfunc2) << endl;    //235
    cout << accumulate(v.begin(), v.end(), 100, myfunc1()) << endl;  //190
    cout << accumulate(v.begin(), v.end(), 100) << endl;//145
}

accumulate源码,有两个版本,根据传进来的参数决定走哪个版本

//参数4为空,默认做累加操作
template<class InputIterator,class T>
T accumulate(InputIterator first,InputIterator last,T init)
{
    for(;first!=last;++first)
        init=init+*first;
    return init;
}
//参数4传入自定义函数或者仿函数
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;
}

 for_each算法

C++11还有一种遍历容器的简单方式

    vector<int>v;
    v.push_back(2);
    v.push_back(3);
    v.push_back(7);
    //for_each(v.begin(), v.end(), func());
    for (int i: v)
    {
        cout << i << " ";
    }    

replace和replace_if算法

算法使用

class Greater20{               //谓词
    public:
        bool operator()(int val){
            return val >=20;
        }
    };
    vector<int >v;
    v.push_back(20);
    v.push_back(30);
    v.push_back(10);
    v.push_back(20);
    v.push_back(50);
    cout << "替换前" << endl;
    for_each(v.begin(), v.end(), myprint);
    cout << endl;
    cout << "替换后" << endl;
    replace(v.begin(), v.end(), 20, 100); //把容器中所有值为20的替换为100;
    replace_if(v.begin(), v.end(), Greater20(),2000);    //把容器中大于20的替换为2000

算法源码,很简单

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

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(*frist))
            *first=new_value;
}

binary_search用于查找某个元素是否存在,前提是有序序列

 

仿函数

  为算法服务。当用户要求算法有一些独特的准则的时候,比如说排序,算法默认是从小到大,如果我们想要从大到小排序,则需要提供仿函数。算法一般有两个版本,默认版本(没有仿函数),第二版本(有仿函数),编译器会选择正确的版本。仿函数是六大部件中最简单的,也是可以自己写这样的函数融入到标准库中的去的。

  标准库有三大类仿函数,一共24个,以下列举了几个

  算术类

template<class T>
struct plus:public binary_function<T,T,T>
{
    T operator()(const T&x,const T&y)const
    {return x+y;}
};

template<class T>
struct minus:public binary_function<T,T,T>
{
    T operator()(const T&x,const T&y)const
    {return x-y;}
};

  相对关系类

template<class T>
struct equal_to:public binary_function<T,T,bool>
{
    bool operator()(const T&x,const T&y)const
    {return x==y;}
};
template<class T>
struct less:public binary_function<T,T,bool>
{
    bool operator()(const T&x,const T&y)const
    {return x<y;}
};

  逻辑类

template<class T>
struct logical_and:public binary_function<T,T,bool>
{
    bool operator()(const T&x,const T&y)const
    {return x&&y;}
};

  这些标准库中所提供的仿函数,都有继承关系,继承于(binary_function)

template<class Arg1,class Arg2,class Result>
struct binary_function
{
    typedef Arg1 first_argument_type;
    typedef Arg2 second_argument_type;
    typedef Result result_type;
};

  由此可见,他们继承父类一堆typedef。而自定义仿函数往往没有这个继承关系,如果没有继承关系,就表明没有真正融入STL中,自定义的仿函数就没有办法被适配,解决办法就是继承相应的父类。这些仿函数被适配器来改造的时候会被问到typedef定义的东西,类似于算法去问迭代器中的五大特性。


 

适配器

  根据适配器要去改造的对象命名为相应的迭代器,对容器改造就称为容器迭代器,对仿函数改造就称为仿函数迭代器。继承

  一切适配器都有一个共性:当A去改造B,A就代表了B,A所做的事情都有B代劳,A是用户和B之间的桥梁,适配方式有两种:一种是A继承B,另外一种也是最常见的,A内含有B,比如stack和queue,并没重新定义这个两个类,而是他们中内含有deque,通过关闭deque的某些功能改造成了stack和queue。容器迭代器就说明内含有一个容器,仿函数迭代器内存有一个仿函数。

  函数适配器,count_if举例

迭代器适配器

  1、逆向迭代器,以连续空间容器的迭代器为例

以下是用户使用的逆向迭代器

void test02()
{
    vector<int>v;
    v.push_back(1);
    v.push_back(2);
    v.push_back(3);
    v.push_back(4);
    v.push_back(5);
    vector<int>::reverse_iterator it = v.rbegin();  //逆向迭代器it指向5
    it++;//it指向4
    cout << *(it.base()) << endl;//取出对应的正向迭代器,指向5
    cout << *(it + 3) << endl;//it指向1
}

源码

//结合上面例子
reverse_iterator rbegin()
{    
    return reverse_iterator(end());
}

template <class Iterator>
class reverse_iterator
{
protected:
    Iterator current;    //正向迭代器
public:
    typedef typename iterator_traits<Iterator>::iterator_category iterator_category;
    ...
    //五种特性
    typedef Iterator iterator_type;        //正向迭代器类型
    typedef reverse_iterator<Iterator>   self  //逆向迭代器类型
    
public//构造函数
    explicit reverse_iterator(iterator_type x):current(x){}
        reverse_iterator(const self &x):current(x.current){}
    iterator_type base() const { return current;}
    reference operator*() const {Iterator tmp = current; return *--tmp;}//传进来的end()指向5后面的元素
    pointer operator->() const { retrun &(operator*());}
    self &operator++() {--current; return *this;}  //逆向迭代器++,相当于正向迭代器--(此时指向5),返回值再重载*运算符(指向4)
    self &operator--() {++current;return *this;}
    self operator+(difference_type n) const { return self(current - n);}
    self operator-(difference_type n) const { return slef(current +n);}

}

  2、inserter

  在l2中的所有元素拷贝到l1的指定位置

void test03()
{
    list<int>l1;
    list<int>l2;
    for (size_t i = 1; i < 6; i++)
    {
        l1.push_back(i);
        l2.push_back(i + 10);
    }
    list<int>::iterator it = l1.begin();
    advance(it, 3);  //it向前移动3位,不能直接+3
    copy(l2.begin(), l2.end(), inserter(l1, it));//1 2 3 11 12 13 14 15 4 5
}

copy源码

ostream_iterator

void test05()
{
    vector<int>v;
    for (size_t i = 1; i < 6; i++)
    {
        v.push_back(i);
    }
    ostream_iterator<int> out_it(cout, ",");
    copy(v.begin(), v.end(), out_it);
}
//屏幕输出 1,2,3,4,5,

hashfunction

  hashfunction是把元素转换成hashcode,得到的hashcode值越乱越好。当元素是基本数据类型时,编译器中有现成的hashfunction,除了字符型元素,其他类型元素,hashcode就是他本身。如果元素是个自定义数据类型,编译器不知道怎么计算hashcode,这时候需要用户设计一个hashfunction。

#include<iostream>
#include<functional>
#include<unordered_set>
using namespace std;
class PersonHash;
class Person
{
    friend PersonHash;

public:
    Person(int num, string name, int age) :
        m_Num(num), m_Name(name), m_age(age){}
    int m_Num;
    string m_Name;
    int m_age;
};
template<typename T>
//当拆分到最后一个元素
inline void hash_val(size_t&seed, const T&val)
{
    hash_combine(seed, val);
}
template<typename T>
inline void hash_combine(size_t &seed, const T&val)
{
    //改进版的hashfunction
    seed = seed^hash<T>()(val)+0x9e3779b9 + (seed << 6) + (seed >> 2);

}
//2、 接受seed和其他三个参数
template<typename T, typename ...Types>
                                  //分出的第一个参数和其他两个参数
inline void hash_val(size_t &seed, const T &val, const Types &...args)
{
    hash_combine(seed, val);//种子和num  得到一个code
    hash_val(seed, args...);//种子和其他两个参数,递归
}
//1、接受所有参数
template <typename ...Types>
inline size_t hash_val(const Types &...args)
{
    size_t seed = 0;
    hash_val(seed, args...);
    return seed;
}
class PersonHash
{
public:
    std::size_t operator()(const Person& p) const
    {
        return  hash_val(p.m_Num, p.m_Name, p.m_age);
    }
};
//需要指定比较规则
struct eqstr
{
    bool operator()(Person p1, Person p2) const
    {
        return (p1.m_Num == p2.m_Num);
    }
};
int main()
{
    Person p1(1001, "liming", 15);
    Person p2(1002, "jenny", 16);
    Person p3(1003, "denny", 18);
    unordered_set<Person, PersonHash,eqstr> set;
    set.insert(p1);
    set.insert(p2);
    set.insert(p3);
    cout << "篮子数量:" << set.bucket_count() << endl;
    PersonHash hh;
    cout << "liming's pos: " << hh(p1) % 8 << endl;
    cout << "jenny's pos: " << hh(p2) % 8 << endl;
    cout << "denny's pos: " << hh(p3) % 8 << endl;

    cout << "---------------------验证---------------------" << endl;
    for (size_t i = 0; i < set.bucket_count(); i++)
    {
        cout << "bucket #" << i << "  has  " << set.bucket_size(i) <<"个元素"<< endl;
    }
    system("pause");
}

打印结果