STL源碼剖析(四)

functors

仿函數(functor),就是使一個類的使用看上去像一個函數。其實現就是類中實現一個operator(),這個類就有了相似函數的行爲,就是一個仿函數類了。ios

在STL中,將仿函數主要分爲了三大類:算術類(Arithmetic)、邏輯運算類(Logical)和相對關係類(Relational)。less

算術類(Arithmetic)舉例ide

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

...

邏輯運算類(Logical)舉例函數

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

...

相對關係類(Relational)網站

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

...

經過上面的代碼能夠發現,functors都繼承了一個基類。STL規定每一個 Adaptable Function 都應該挑選合適的父類繼承,由於 Adaptable Function 將會提問一些問題。this

template <class Arg, class Result>
struct unary_function
{
    typedef Arg argument_type;
    typedef Result result_type;
};

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

好比上面的less經過繼承binary_function擁有了3個typedef,這些typedef可能會在一些適配器中被詢問到,詳見後面適配器中的源碼。spa

Adapters

Adapters 至關於一種修飾的做用,在容器、迭代器和仿函數的基礎上,對其進行一種改造。將改造完成後的容器、迭代器或仿函數交給用戶使用,但其核心仍是經過內部的容器、迭代器和仿函數進行工做。因此就存在: Container Adapters, Iterator Adapters 和 Functor Adapters三類。指針

容器適配器:stack, queue

stackcode

template <class T, class Sequence=deque<T>>
class stack{
...
public:
    typedef typename Sequence::value_type value_type;
    typedef typename Sequence::size_type size_type;
    typedef typename Sequence::reference reference;
    typedef typename Sequence::const_reference const_reference;
proctected:
    Sequence c;// 底層容器
public:
    bool empty() const { return c.empty(); }
    size_type size() const { return c.size(); }
    reference top() { return c.back(); }
    const_reference top const { return c.back(); }
    void push(const value_type& x) { c.push_back(x); }
    void pop { c.pop_back(); }
};

queue對象

template <class T, class Sequence=deque<T>>
class queue{
...
public:
    typedef typename Sequence::value_type value_type;
    typedef typename Sequence::size_type size_type;
    typedef typename Sequence::reference reference;
    typedef typename Sequence::const_reference const_reference;
proctected:
    Sequence c;// 底層容器
public:
    bool empty() const { return c.empty(); }
    size_type size() const { return c.size(); }
    reference front() { return c.front(); }
    const_reference front() const { return c.front(); }
    reference back() { return c.back(); }
    const_reference back() const { return c.bakc(); }
    void push(const value_tyoe& x) { c.push_back(x); }
    void pop { c.pop_back(); }
};

函數適配器:binder2nd, not1

對於這樣一行語句:

cout << count_if(vi.begin(), vi.end(), not1(bind2nd(less<int>(), 40));

首先須要注意的是less<int>()這並非函數的調用,而是生成一個less<int>的對象!

count_if

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

bind2nd

template <class Operation, class T>
inline binder2nd<Operation> bind2nd(const Operation& op,const T& x)
{
    typedef typename Operator::second_argument_type arg2_type;
    return binder2nd<Operation>(op, arg2_type(x));//返回一個binder2nd<Operation>對象!
}

binder2nd

template <class Operation>
class binder2nd : public unary_function<typename Operation::first_argument_type,
                                        typename Operation::result_type>
{
protected:
    Operation op;
    typename Operation::second_argument_type value;
public:
    binder2nd(const Operation& x,const typename Operation::second_argument_type& y): op(x), value(y){}
    typename Operation::result_type operator()(const typename Operation::first_argument_type& x) const
    {
        return op(x,value);//這裏纔是函數的調用
    }
};

在這些代碼中能夠看到適配器在詢問仿函數一些問題,這些問題就是仿函數繼承的基類中的typedef。全部能回答出這些問題的仿函數都稱爲 Adaptable Function

not1

template <class Predicate>
inline unary_negate<Predicate> not1(consat Predicate& pred)
{
    return unary_negate<Predicate>(pred);
}

template <class Predicate>
class unary_negate : public unart_function<typename Predicate::argument_type, bool>
{
protected:
    Predicate pred;
public:
    eplicit unary_negate(const Predicate& x) : pred(x) {}
    bool operator()(const typename Predicate::argument_type& x) const
    {
        return !pred(x);
    }
};

新型適配器,bind

bind 使用例子(摘自cplusplus網站)

#include <iostream>     // std::cout
#include <functional>   // std::bind

// a function: (also works with function object: std::divides<double> my_divide;)
double my_divide (double x, double y) {return x/y;}

struct MyPair {
  double a,b;
  double multiply() {return a*b;}
};

int main () {
  using namespace std::placeholders;    // adds visibility of _1, _2, _3,...

  // binding functions:
  auto fn_five = std::bind (my_divide,10,2);               // returns 10/2
  std::cout << fn_five() << '\n';                          // 5

  auto fn_half = std::bind (my_divide,_1,2);               // returns x/2
  std::cout << fn_half(10) << '\n';                        // 5

  auto fn_invert = std::bind (my_divide,_2,_1);            // returns y/x
  std::cout << fn_invert(10,2) << '\n';                    // 0.2

  auto fn_rounding = std::bind<int> (my_divide,_1,_2);     // returns int(x/y)
  std::cout << fn_rounding(10,3) << '\n';                  // 3

  MyPair ten_two {10,2};

  // binding members: member function 其實有個 argument: this
  auto bound_member_fn = std::bind (&MyPair::multiply,_1); // returns x.multiply()
  std::cout << bound_member_fn(ten_two) << '\n';           // 20

  auto bound_member_data = std::bind (&MyPair::a,ten_two); // returns ten_two.a
  std::cout << bound_member_data() << '\n';                // 10

  return 0;
}

std::bind 能夠綁定:

  1. functions
  2. function objects
  3. member functions, _1 必須是某個object地址
  4. data members, _ 必須是某個object地址

因此能夠如今能夠用bind替換bind2nd,改寫以下:

vector<int> v {15,37,94,50,73,58,28,98};
int n = count_if(v.cbegin(), v.cend(), not1(bind2nd(less<int>, 50)));
cout << "n=" << n << endl;//5

vector<int> v {15,37,94,50,73,58,28,98};
int n = count_if(v.cbegin(), v.cend(), not1(bind(less<int>, _1, 50)));
cout << "n=" << n << endl;//5

迭代器適配器 reverse_iterator, inserter

reverse_iterator:用來實現去反向指針rbegin(), rend()的實現。

reverse_iterator rbegin() { return reverse_iterator(end()); }

reverse_iterator rend() { returun reverse_iterator(begin()); }

template <class Iterator>
class reverse_iterator
{
protected:
    Iterator current;//對應的正向迭代器
public:
    //逆向迭代器的5中 associated types 都和對應的正向迭代器相同
    typedef typename iterator_traits<Iterator>::iterator_category iterator_category;
    typedef typename iterator_traits<Iterator>::value_type value_type;
    ...
    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; 
    }
    pointer operator->() const { return &(operator*()); }

    //前進便後退,後退便前進
    self& operator++() { --current; return *this; }
    self& operator--() { ++current; return *this; }
    slef operator+(difference_type n) const { return self(current - n); }
    slef operator-(difference_type n) const { return self(current + n); }
};

inserter: 將iteartor中的複製操做改成插入操做,而且將iteartor右移一個位子。可讓用戶執行表面上assign而實際上insert的行爲。

template <class Container>
class insert_iterator
{
protected:
    Container* container;
    typename Container::iterator iter;
pbulic:
    typedef output_iterator_tag iterator_category;
    insert_iterator(Container& x, typename Container::iterator i) : container(&x), iter(i) {}
    //對賦值操做符重載,以實現插入
    insert_iterator<Contain>& operator=(const typename Container::value_type& value)
    {
        iter = container->insert(iter, value);
        ++iter;
        return *thisl
    }
};

//輔助函數,幫助用戶使用insert_iterator
template <class Container, class Iterator>
inline insert_iterator<Container> inserter(Container& x, Iterator i)
{
    typedef typename Container::iterator iter;
    return insert_iterator<Container>(x, iter(i));
}

特殊適配器 ostream_iterator, istream_iterator

ostream_iterator

先來看一個例子

#include <iostream>     //std::cout
#include <iterator>     //std::ostream_iterator
#include <vector>       //std::vector
#include <algorithm>    //std::copy

int main()
{
    std::vector<int> myvector;
    for (int i = 1; i < 10; ++i) myvector.push_back(i * 10);

    std::ostream_iterator<int> out_it(std::cout, ",");
    std::copy(myvector.begin(), muvector.end(), out_it);
    return 0;
}

輸出結果:

10,20,30,40,50,60,70,80,90,

首先來看看copy這個函數作了什麼

template<class InputIterator first, InputIterator last,OutputIterator result>
copy(InputIterator first,InputIterator last, OutputIterator result)
{
    while(first != last)
    {
        *result = *first;
        ++result;
        ++first;
    }
}

再對比着ostream_iterator的源碼,就能分析出輸出的緣由:

template<class T,class chatT=char,class traits=char_traits<charT>>
class ostream_iterator:public iterator<output_iterator_tag,void,void,void,void>
{
    basic_ostream<charT,traits>* out_stream;
    const charT* delim;
public:
    typedef charT char_type;
    typedef traits traits_type;
    typedef basic_ostream<charT,traits> ostream_type;
    ostream_iterator(ostream_type& s):out_stream(&s),delim(0){}
    ostream_iterator(onstream_type&s, const charT* delimiter):out_stream(&s),delim(delimiter){}
    ostream_iterator(const ostream_iterator<T,charT,traits>& x):ostream_iterator(x.out_stream),delim(x.delim){}
    ~ostream_iterator(){}
    ostream_iterator<T,chatT,traits>& operator=(const T& value){//關鍵點!!
        *out_stream << value;
        if(delim!=0) 
            *out_stream << delim;
        return *this;
    }

    ostream_iterator<T,charT,traits>& operator*(){return *this;}
    ostream_iterator<T,charT,traits>& operator++(){return *this;}
    ostream_iterator<T,charT,traits>& operator++(int){return *this;}
};

關鍵就在於重載了=號運算符。

istream_iterator

仍是先看一個例子

#include <iostream>
#include <iterator>

int main()
{
    double value1, value2;
    std::cout << "Please, insert two values:";
    std::istream_iterator<double> eos;
    std::istream_iterator<double> iit(std::cin);//當建立對象時,就已經在要求輸入了
    if (iit != eos)
        value1 = *iit;
    ++iit;
    if (iit != eos)
        value2 = *iit;
    std::cout << value1 << "*" << value2 << "=" << (value1*value2) << "\n";
    return 0;
}

這個例子就是一個簡單的乘法,其中std::istream_iterator<double> iit(std::cin);至關於cin >> value;。具體的原理仍是看源碼吧。

template <class T, class charT = char, class traits = char_traits<charT>>, class Distance = ptrdiff_t >
class istream_iterator : public iterator<input_iterator_tag, T, Distance, const T*, const T&>
{
    basic_istream<charT, traits>* instream;
    T value;
public:
    typedef charT char_type;
    typedef traits traits_type;
    typedef basic_istream<charT, traits> istream_type;
    istream_iterator() :instream(0) {}
    istream_iterator(istream_type& s) :in_stream(&s) { ++*this; }
    istream_iterator(const istream_iterator) < T, charT, traits, Distance > & x):in_stream(x.in_stream), value(x.value){}
    ~istream_itetator() {}
    const T& operator*() const { return value; }
    const T* operator->() const { return value; }
    istream_iterator<T, charT, traits, Distance>& operator++() {
        if (in_stream && !(*in_stream >> value))
            in_stream = 0;
        return *this;
    }
    istream_iterator<T,charT,traits,Distance>operator++(int) {
        istream_iterator<T, charT, traits, Distance> tmp = *this;
        ++*this;
        return tmp;
    }
};

對照源碼能夠發現,std::istream_iterator<double> iit(std::cin);這裏調用了istream_iterator的++符號,此時就已經開始輸入了。

相關文章
相關標籤/搜索