1、仿函数有很多名字,比如函数对象,functional objects,functor,这些都是指代一个东西。
2、仿函数的优势是可以保存状态,其表面实现方式是在class对象内部重载operator()函数,模仿了函数foo()的形式来实现函数功能。
比如void foo()转换成functor的基本形式:
class FunctionObejectType
{
public:
void operator(){.......}
int status;
}
3、C++中,仿函数比(非内联)函数指针效率高。不过在非STL场合,推荐用函数指针,比如纯C环境用了大量的函数指针,效率非常高。
4、注意传入目标函数的STL仿函数,是通过值传递进来的,被复制了一份,目标函数内部对仿函数的修改,不影响原来的仿函数。
5、如果想要使得传入STL的仿函数发生变化,使用引用传递。for_each()函数的返回值就是改变后的仿函数。注意怎么使用引用传递,比如generate_n函数:
IntSequenceseq(1);//IntSequence是一个仿函数
generate_n<back_insert_iterator<list<int>> ,int, IntSequence&>(back_inserter(coll), 4, seq);
要在模板列表明确标示出是引用。
6、《C++标准程序库》P303 说明remove_if有一个问题,因为其内部实现的find_if会把仿函数再次复制一份,如果编写的仿函数会自动变化,那么每次执行find_if函数使用的仿函数不会还是原版不会变化,从而产生bug,所以你最好自己重新做一个remove_if。另外这个例子也说明,仿函数的operator()重载最好是const的,不然会出现很多莫名其妙的问题。
7、加入#include<functional> 可以使用很多已经写好的仿函数,并且可以使用bind系列函数。vs2008 sp1的tr1标准、boost和C++11的新bind函数有更强大的功能。
8、bind系列和not1,not2函数就是所谓的函数配接器。03标准里面有bind1st和bind2nd函数。灵活性已经很强了,不过只适用于仿函数,普通函数需要转换,见第10条。例子:
pos = find_if(coll.begin(), coll.end(), not1(bind2nd(modulus<int>(), 2)));//返回第一个偶数
9、成员函数的配接器,就是直接使用成员函数来完成STL的功能部分。使用方法有两种:
a) mem_fun_ref(op),op是对象的一个成员函数的引用。用法:for_each(coll.begin(),coll.end(), bind2nd(mem_fun_ref(&Persom::print), “it is ”));
b) mem_fun(op),op是对象指针的成员函数,注意这种情况下容器中存放的是对象指针。
这里有一个历史问题,就是mem_fun其实最好应该命名为mem_fun_ptr,不过最终没有这样做,很遗憾。
10、ptr_fun()可以把普通函数变为配接器可用组件。例子:
pos =find_if(coll.begin(), coll.end(), not1(ptr_fun(check)));
11、自定义仿函数,必须得继承unary_function和binary_function模板,才能使用配接器。
12、组合型函数(很强大的配接器)没有纳入STL标准,不追求效率可以自己实现,很简单。请参考《STL源码剖析》8.4.3节的实现。其接口有如下几种:
接口 |
本书名称 |
SGI STL标准 |
f(g(elem)) |
compose_f_gx |
compose1 |
f( g(elem1, elem2) ) |
compose_f_gxy |
|
f( g(elem) , h(elem) ) |
compose_f_gx_hx |
compose2 |
f( g(elem1) , h(elem2) ) |
compose_f_gx_hy |
|
书上的例子充分体现了函数也是对象的思想。其应用和C的函数指针有异曲同工之妙。