C++源码剖析和泛型编程(侯捷)(八) 仿函数和适配器

仿函数

当你要求一些特殊的准则时,就需要一般函数或仿函数的形式来告诉算法。

  1. 又称函数对象,为算法提供一些自定义的函数规则(例如以特定的条件实现累计 accumulate)。
  2. 规模较小,比较容易由用户自己实现,并加入标准库中作为自己应用的部分。
  3. 仿函数本质是一个类(struct),类中必须实现重载小括号(operator())使用时通过加上()来成为临时对象调用。

分类:算术类(加减),逻辑运算类(与或非),相对关系类(比大小)。

标准库所提供的仿函数都有继承关系,用户定义的 functor 如果没有继承特定的类,那么就没有融入 STL 的体系结构里面。之后的操作也许会失败。

C++源码剖析和泛型编程(侯捷)(八) 仿函数和适配器_第1张图片

仿函数functors的可适配条件

就是可以回答三个问题。1/2参数类型,结果参数类型。

C++源码剖析和泛型编程(侯捷)(八) 仿函数和适配器_第2张图片

继承的 typedef 这个技巧在标准库中多次出现,由 less 继承 binary_function(本身只有 typedef) 不会带来额外的开销。

仿函数能被修改,被适配(adaptable),被调整的话就要选择适当的 u / b function 来继承,才能真正融合到 STL 中。被 adapter 改造时 adapter 可能会提问这些 typedef 的问题(类比于算法提问迭代器中 typedef 的五个问题)。

内含的方式去获得需要调整的东西。

容器适配器

stack和queue通过内含一个sequence(默认为deque)来实现。

函数适配器:binder2nd

bind2nd(less(),40)这里并没有调用函数,这里只是将40和操作记录下来,直到被调用时才将value绑定为临时对象的第二参数,一起传递给count_if。bind2nd是辅助函数,完成的还是class的功能。函数适配器修改函数后仍然要表现出函数的特性。

这里OperatingC++源码剖析和泛型编程(侯捷)(八) 仿函数和适配器_第3张图片

模板中Operation这个类型实际就是less()这个类类型,比较麻烦,因此用户bind2nd来辅助,利用函数实参推导性质来推导。

继承了unary_function,比如检测是否传入的是int,这些是由灰色部分来完成的。第一实参是什么类型,第二实参什么类型,比完之后什么类型,三个问题, 能够回答这三个问题,我们则称这个function为adaptable

typename帮助代码在各家编译器上编译通过。 函数functor修饰函数后它也要回答问题,因为绑定后,就只剩一个参数了,所以它要继承unary_function。

新型适配器:bind

 C++源码剖析和泛型编程(侯捷)(八) 仿函数和适配器_第4张图片  

占位符,_1,_2。

bind() 若没有类型参数时,函数 my_divide() 的返回类型就默认是 bind() 的返回类型。比如为给定int时,默认就是my_divide()中的double

my_divide如以下代码写的话bind便是绑定一个函数对象。

//只需将上述 my_divide() 函数改造成以下函数对象即可
template 
struct divides : public binary_function  {
T operator(const T& x, const T& y) 
    { return x / y ;}
} my_divide;

multiply()看上去没有形参,但实际上会有一个参数:this。当用_1时代表还未绑定,因此要指定参数。

bound_memdata2()是绑定数据成员,取出对象中的第二个成员。

C++源码剖析和泛型编程(侯捷)(八) 仿函数和适配器_第5张图片

迭代器适配器:reverse_iterator

因为是以正向迭代器为基础,所以迭代器需要提供的5种相关类型是一致的。只是需要将一些正向中的一些操作或运算符重载。引入temp是因为current是const,不可改变的。

C++源码剖析和泛型编程(侯捷)(八) 仿函数和适配器_第6张图片

迭代器适配器:inserter

C++源码剖析和泛型编程(侯捷)(八) 仿函数和适配器_第7张图片

链表不是连续空间,所以不能直接迭代器+3,只能调用advance。

copy在这里是直接拷贝,不会使空间发生变化,所以程序里传入的是inserter(foo,it),将其套入adaptor中,借由辅助函数完成。借由操作符重载来改变已经写好的copy函数。        

X适配器:ostream_iterator

这一页ppt中改了前++和后++,使其不操作,上一图中++iter我认为和++result重复,考虑或许是未将重载++操作符代码截出来。

传入参数为指向 cout 的指针,返回值为 *this,都是为了保证输入的 cout 确实被作用,如果传入的是形参,则作用过后原值不会被修改。

 X适配器:istream_iterator

创建了两个对象eos和iit,eos没有参数,代表end of stream的符号,iit是有参数的,std::cin。    

iit (std::cin) 在初始化运行构造函数时,就已经开始读取数据了。   

C++源码剖析和泛型编程(侯捷)(八) 仿函数和适配器_第8张图片以上代码说明同一个copy搭配不同的adaptor有不同的行为表现。

当istream_iteratoriit(cin),eos;从源码中我们可以发现创建时已经开始读取数据了。如果在这句话后写上“请开始输入”的话,这句话运行时除非用户先输入一个数,否则是不会出现的。                                                        、

 

                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      

你可能感兴趣的:(C++源码剖析和泛型编程(侯捷)(八) 仿函数和适配器)