简介
仿函数(functor)就是定义了operator()的一种型别(或实例化的对象),可以如下使用
FunctionObjectType fo;
...
fo(...) //并不是使用函数fo(),而是调用fo的operator()函数
明显其和函数的很像,只不过函数是在函数体内实现,而其实在operator()里实现相关功能,伪代码如下
class FunctionObjectType
{
public:
operator()
{
...
}
}
显然仿函数更复杂,那么必然会有其优势的
1. 仿函数更灵活,因为其是一种型别,可以拥有状态
2. 每个仿函数都是一种型别,此可以把它当作tmpelate的参数进行传递,这个是普通函数做不到的,从而指定某种行为模式,还有一个好处就是容器的型别也会因仿函数的不同而不同
3. 仿函数比一般函数指针快(深表怀疑!!!有时间试试)
仿函数可当排序准则
见如下例子
#include <iostream>
#include <ostream>
#include <iterator>
#include <string>
#include <set>
using namespace std;
class Person
{
public:
string first_name;
string last_name;
Person(const string& str1, const string& str2)
: first_name(str1), last_name(str2){}
};
class PersonSort
{
public:
bool operator() (const Person& p1, const Person& p2) const //开始忘写()
{
return (p1.first_name < p2.first_name) || (!(p1.first_name < p2.first_name) && (p1.last_name < p2.last_name));
}
};
int main(void)
{
Person p1("wang","er");
Person p2("zhao","si");
Person p3("wang","san");
set<Person,PersonSort> col; //为什么可以这样,可以看看set的定义
col.insert(p1);
col.insert(p2);
col.insert(p3);
set<Person,PersonSort>::iterator pos;
for (pos=col.begin(); pos!=col.end(); ++pos)
//cout << *pos << " "; //开始这里出错了,要想不错就得重载operator<<运算符
cout << "name : " << pos->first_name << ' ' << pos->last_name << endl;
return 0;
}
/*
输出:
wang er
wang san
zhao si
*/
关于仿函数的operator()返回值?这个其实是看具体要求的,你需要用到的仿函数的地方,且带入算法时不需要带入参数,其中仿函数的operator()的参数是算法自动把其区间的元素值带进去进行运算的。
拥有内部状态的仿函数
见下例子
#include <iostream>
#include <algorithm>
#include <iterator>
#include <list>
using namespace std;
class IntSequence
{
private:
int _value;
public:
IntSequence(int i) : _value(i) {}
int operator() () //返回值为什么得是int而不是void,因为后面的generate_n()里的参数这样要求的
{
return _value++;
}
};
template<typename T>
inline void PRINT_ELEMENTS(const T& col, const char* str=" ")
{
typename T::const_iterator pos;
cout << str;
for (pos=col.begin(); pos!=col.end(); ++pos)
cout << *pos << ' ';
cout << endl;
}
int main(void)
{
list<int> col;
generate_n(back_inserter(col),9,IntSequence(1));
//generate_n(col.begin(),9,IntSequence(1)); //为什么这样打印不出来,因此之前初始化的col根本就没元素
PRINT_ELEMENTS(col); // 1 2 3 4 5 6 7 8 9
generate(++col.begin(),--col.end(),IntSequence(42));
PRINT_ELEMENTS(col); // 1 42 43 44 45 46 47 48 9
return 0;
}
下是generate_n的代码,因此可以看出
template<typename _OutputIterator, typename _Size, typename _Generator>
_OutputIterator
generate_n(_OutputIterator __first, _Size __n, _Generator __gen)
{
// concept requirements
__glibcxx_function_requires(_OutputIteratorConcept<_OutputIterator,
// "the type returned by a _Generator"
__typeof__(__gen())>)
for (__decltype(__n + 0) __niter = __n;
__niter > 0; --__niter, ++__first)
*__first = __gen();
return __first;
}
仿函数是pass by value(传值),而不是pass by reference(引用),因此算法并不会改变随参数而来的仿函数的状态。其实算法当然是可以改变仿函数的状态,但由于传递的只是一个副本,改变的也只是一个副本。
如要改变仿函数的状态,有两种方法
1. 指明pass by reference传递仿函数
2. for_each的回返值
pass by reference传递仿函数
调用算法时指明仿函数型别是个reference型别
#include <iostream>
#include <algorithm>
#include <iterator>
#include <list>
using namespace std;
class IntSequence
{
private:
int _value;
public:
IntSequence(int i) : _value(i) {}
int operator() () //返回值为什么得是int而不是void,因为后面的generate_n()里的参数这样要求的
{
return _value++;
}
};
template<typename T>
inline void PRINT_ELEMENTS(const T& col, const char* str=" ")
{
typename T::const_iterator pos;
cout << str;
for (pos=col.begin(); pos!=col.end(); ++pos)
cout << *pos << ' ';
cout << endl;
}
int main(void)
{
list<int> col;
IntSequence seq(1);
generate_n<back_insert_iterator<list<int> >, int, IntSequence& >
(back_inserter(col),4,seq); //调用算法时指明是引用就可以了
PRINT_ELEMENTS(col); // 1 2 3 4
generate_n(back_inserter(col),4,IntSequence(42));
PRINT_ELEMENTS(col); // 1 2 3 4 42 43 44 45
generate_n(back_inserter(col),4,seq);
PRINT_ELEMENTS(col); // 1 2 3 4 42 43 44 45 5 6 7 8
return 0;
}
for_each的回返值
例子
#include <iostream>
#include <algorithm>
#include <iterator>
#include <vector>
using namespace std;
class MeanValue
{
private:
long _num; //计数
long _sum; //和
public:
//MeanValue(long num, long sum) : _num(num), _sum(sum) {}
MeanValue() : _num(0), _sum(0) {}
void operator() (int elem) //返回值为什么得是int而不是void,因为后面的generate_n()里的参数这样要求的
{
_num++;
_sum += elem;
}
double value()
{
return static_cast<double>(_sum) / static_cast<double>(_num);
}
};
template<typename T>
inline void PRINT_ELEMENTS(const T& col, const char* str=" ")
{
typename T::const_iterator pos;
cout << str;
for (pos=col.begin(); pos!=col.end(); ++pos)
cout << *pos << ' ';
cout << endl;
}
int main(void)
{
vector<int> col;
for (int i=1; i<9; ++i)
col.push_back(i);
MeanValue mv = for_each(col.begin(),col.end(),MeanValue()); //刚开始提出错误,因为之前写MeanValue时没有提供默认构造函数
cout << "mean value : " << mv.value() << endl;
return 0;
}
判断式和仿函数
所谓判断式就是返回布尔值的一个函数或仿函数,见下例子
#include <iostream>
#include <list>
#include <algorithm>
#include <iterator>
using namespace std;
class Nth
{
private:
int nth;
int count;
public:
Nth (int n) : nth(n), count(0){}
bool operator() (int)
{
return ++count == nth;
}
};//改仿函数就是移除第nth个元素
int main(void)
{
list<int> col;
for (int i=1; i<=9; ++i)
col.push_back(i);
copy(col.begin(),col.end(),ostream_iterator<int>(cout," ")); // 1 2 3 4 5 6 7 8 9
cout << endl;
list<int>::iterator pos;
pos = remove_if(col.begin(),col.end(),Nth(3)); // 移除3
col.erase(pos,col.end());
copy(col.begin(),col.end(),ostream_iterator<int>(cout," "));
cout << endl;
return 0;
}
奇怪?按道理应该只移除一个3,可是却移除了3和6,为什么?
先看看remove_if的源代码,见下
这个算法使用find_if()来搜寻被移除的第一个元素,然而,接下来它使用传进来的判断式op的副本去处理剩余的元素,这时原始状态下的Nth再一次被使用,因而会移除剩余的第3个元素,也就是整体的第6个元素。
还有,见图把,解释不清楚了。总的来说就是你的仿函数设计的不合理,不应该改变判断式的状态。
总结
仿函数就是写一个class,然后为这个class重载一个operator()操作就可以了,至于operator() 函数的返回值及参数值你就看算法的需要而定了。