functors

仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了。

在STL中,将仿函数主要分为了三大类:算术类(Arithmetic)、逻辑运算类(Logical)和相对关系类(Relational)。

算术类(Arithmetic)举例

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 将会提问一些问题。

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可能会在一些适配器中被询问到,详见后面适配器中的源码。

Adapters

Adapters 相当于一种修饰的作用,在容器、迭代器和仿函数的基础上,对其进行一种改造。将改造完成后的容器、迭代器或仿函数交给用户使用,但其核心还是通过内部的容器、迭代器和仿函数进行工作。所以就存在: Container Adapters, Iterator Adapters 和 Functor Adapters三类。

容器适配器:stack, queue

stack

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/2std::cout << fn_five() << '\n';                          // 5auto fn_half = std::bind (my_divide,_1,2);               // returns x/2std::cout << fn_half(10) << '\n';                        // 5auto fn_invert = std::bind (my_divide,_2,_1);            // returns y/xstd::cout << fn_invert(10,2) << '\n';                    // 0.2auto fn_rounding = std::bind<int> (my_divide,_1,_2);     // returns int(x/y)std::cout << fn_rounding(10,3) << '\n';                  // 3MyPair ten_two {10,2};// binding members: member function 其实有个 argument: thisauto bound_member_fn = std::bind (&MyPair::multiply,_1); // returns x.multiply()std::cout << bound_member_fn(ten_two) << '\n';           // 20auto bound_member_data = std::bind (&MyPair::a,ten_two); // returns ten_two.astd::cout << bound_member_data() << '\n';                // 10return 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;//5vector<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::copyint 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的++符号,此时就已经开始输入了。

转载于:https://www.cnblogs.com/joker-wz/p/10299617.html

STL源码剖析(四)相关推荐

  1. STL源码剖析---红黑树原理详解下

    转载请标明出处,原文地址:http://blog.csdn.net/hackbuteer1/article/details/7760584       算法导论书上给出的红黑树的性质如下,跟STL源码 ...

  2. STL源码剖析 map

    所有元素会根据元素的键值自动被排序 元素的类型是pair,同时拥有键值和实值:map不允许两个元素出现相同的键值 pair 代码 template <class T1,class T2> ...

  3. STL源码剖析 空间配置器 查漏补缺

    ptrdiff_t含义 减去两个指针的结果的带符号整数类型 ptrdiff_t (Type support) - C 中文开发手册 - 开发者手册 - 云+社区 - 腾讯云 std::set_new_ ...

  4. 《STL源码剖析》相关面试题总结

    一.STL简介 STL提供六大组件,彼此可以组合套用: 容器 容器就是各种数据结构,我就不多说,看看下面这张图回忆一下就好了,从实现角度看,STL容器是一种class template. 算法 各种常 ...

  5. STL源码剖析(十三)关联式容器之rb_tree

    STL源码剖析(十三)关联式容器之rb_tree 文章目录 STL源码剖析(十三)关联式容器之rb_tree 一.rb_tree的数据结构 二.rb_tree的迭代器 三.rb_tree的操作 3.1 ...

  6. C++ STL源码剖析 笔记

    写在前面 记录一下<C++ STL源码剖析>中的要点. 一.STL六大组件 容器(container): 各种数据结构,用于存放数据: class template 类泛型: 如vecto ...

  7. 【STL源码剖析】迭代器

    [STL源码剖析]迭代器 第3章 迭代器(iterators)与traits编程技法(<STL源码剖析> ) 3.1 迭代器设计思维--STL关键所在 3.2 迭代器(iterator)是 ...

  8. 【《STL源码剖析》提炼总结】 第1节:空间配置器 allocator

    文章目录 一. 什么是空间配置器 二. STL allocator的四个操作: allocate,deallocate,construct,destroy `construct()` `destroy ...

  9. STL源码剖析---移动advance

    目录 一. 迭代器 二. 类型萃取机 2.1 处理类实现的迭代器 2.2 处理原生指针实现的迭代器 三. advance 3.1 iterator_category 3.2 激活重载机制 3.3 实现 ...

  10. STL(C++标准库,体系结构及其内核分析)(STL源码剖析)(更新完毕)

    文章目录 介绍 Level 0:使用C++标准库 0 STL六大部件 0.1 六大部件之间的关系 0.2 复杂度 0.3 容器是前闭后开(左闭右开)区间 1 容器的结构与分类 1.1 使用容器Arra ...

最新文章

  1. MegEngine计算图、MatMul优化解析
  2. 年终收藏!一文看尽2020年度最出圈AI论文合集
  3. 网络推广——网络推广专员如何提升企业网站转化率?
  4. 金融领域下的数据挖掘算法应用:XGboost模型
  5. 【Java面试题视频讲解】十六进制转十进制
  6. svn的搭建和和文件检出与提交
  7. 【SQL】存储过程procedure 触发器trigger
  8. 数学图形之SineSurface与粽子曲面
  9. 棋盘问题 POJ - 1321
  10. 14006.xilinx-SDK在线jtag调试
  11. Python:程序设计方法学、体育竞技分析
  12. httpservletresponse 重定向浏览器不变的原因_JavaWeb——Servlet——请求转发与响应重定向...
  13. Matlab多惯量仿真,两连杆机器鱼的简单建模以及MATLAB仿真
  14. C#读取所有PC中所有进程
  15. Matlab中获取文件夹下所有子文件夹名称操作
  16. 安装Powerdesigner16.5
  17. Java第32课——求数组元素最大值
  18. telnet登录交换机
  19. jquery 漂浮广告
  20. python mpi4py multiprocessing_python基于multiprocessing的多进程创建方法

热门文章

  1. C语言解决汉诺塔问题
  2. 【CyberSecurityLearning 52】Web架构安全分析(web工作机制、HTTP协议)
  3. JDBC修改数据库(Object类的应用)
  4. 一个历史遗留问题,引发的linux内存管理的‘血案’
  5. Unity3D ShaderLab 物体相交
  6. XML解析---JAVAEE学习之路
  7. 图的两种存储形式(邻接矩阵、邻接表)
  8. dijkstra+堆优化
  9. 【译】ICO 2.0 — The Advent of What Crypto-Fund Raising Should Really Look Like
  10. android代码 根据黑名单拦截短信,滴滴将小范围测试短信报警 司、乘两端同步试运行“黑名单”...