引子
先考虑一个简单的例子:假设有一个vector<string>
,你的任务是统计长度小于5的string
的个数,如果使用count_if
函数的话,你的代码:
bool LessThan5Function(const string& str) {//寻找长度小于5的字符串
return str.length() < 5;
}
void test() {
vector<string> sVec{ "sadaw","srfafas","12" };
int count = count_if(sVec.begin(), sVec.end(), LessThan5Function);//寻找容器中长度小于5的字符串的数量
}
在这里count_if
函数的第三个参数是一个函数指针,返回一个bool
类型的值。一般的,如果需要指定这个长度len
作为参数传入的话,我们可能将函数写成这样:
bool LessThan5Function(const string& str,size_t len) {//寻找长度小于5的字符串
return str.length() < len;
}
这个函数看起来比前面一个版本看起来OK,因为更加具有可操作性,但是他不能满足这个泛型函数count_if
的参数要求:count_if
要求的函数是unary function
(仅带有一个参数的函数)。
我们如何才能在只能传入一个参数的情况下使用这个count_if
,并且指定自己想要的参数,而且,不需要改变这个函数本身,比如如果我们要小于7的字符串数量,就得将传入的函数写成这样的话,正常人都会崩溃的
bool LessThan7Function(const string& str) {
return str.length() < 7;
}
一般这种情况怎么办?
我们应该会想到用一个全局变量
int maxLength = 5;
bool LessThan5Function(const string& str) {//寻找长度小于5的字符串
return str.length() < maxLength;
}
但是全局变量的缺点相信大家都很清楚:
- 污染命名空间(多人协同,冲突)
- 每次修改功能还是需要去修改这个全局变量
- 不具有重复使用的功能(比如多个要用到这个函数指针的泛函数)
所以,说了这么多,我们需要的是一个什么东西呢?
我们需要一个能够实现函数功能,并且可以携带我们需要的数据(比如上面的字符串长度),最后最重要的:不会因为多个人同时使用,而造成功能冲突和混乱,的这样的一个东西
这就是我将要介绍的仿函数:
仿函数
借用一下百度百科的解释:
仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是在类中实现一个
operator()
,这个类就有了类似函数的行为,就是一个仿函数类了。
将上面的那个函数,用仿函数的方式改写了之后是什么样子?
class LessThan5Functor {
public:
bool operator ()(const string& str) {//寻找长度小于5的字符串
return str.length() < 5;
}
};
就是一个简单的operator ()
操作符的重写,但是仿函数和上面的函数指针不同的一点就是:它是一个对象
什么意思呢?
就是这是一个可以被实例化的、具有内存的、可存储数据的实体
想一下,上面的函数指针,我们想要携带数据进入那个count_if
是多么的麻烦和不具有普适性,而仿函数的出现,就是为了兼容STL
的标准
仿函数的使用
int main()
{
greater<int > gt;
cout << gt(1, 2) << endl;//false; (1)
cout << greater<int >()(6, 4) << endl;//true; (2)
system("pause");
return 0;
}
上面的代码,就是一个仿函数的使用例子,我们定义了一个greater<int>
的对象gt
,(1) 中gt(1, 2)
就是直接调用了greater<int> gt
的的operator ()
函数
而(2)是定义了一个greater<int>
的局部变量,然后调用其operator ()
函数,在(2)的这行代码结束后,这个greater<int>
的局部变量就被回收了(临时对象)
善用仿函数的成员变量
针对最开始我们说的多个参数问题,我们可以如何解决呢?
int count = count_if(sVec.begin(), sVec.end(), LessThan5Functor);//寻找容器中长度小于5的字符串的数量
我们可以传入一个LessThanLenFunctor
的仿函数对象作为参数,这个对象内部保存了长度信息,LessThanLenFunctor
的对象实现如下:
class LessThanLenFunctor {
public:
bool operator ()(const string& str) {//寻找长度小于5的字符串
return str.length() < len;
}
LessThanLenFunctor(int l):len(l) {}
private:
int len ;
};
这样我们就避免了使用全局变量的那几个问题,而且,非常优雅且有用,我们如何去使用这个仿函数呢?
如下,假设我们想查找长度小于3的字符串:
void test() {
vector<string> sVec{ "sadaw","srfafas","12" };
int count = count_if(sVec.begin(), sVec.end(), LessThanLenFunctor(3));//寻找容器中长度小于3的字符串的数量
}
我们只需要传入一个内部成员变量len
为3的仿函数对象就行了,无论长度如何变化,有多少人要使用多少个不同的仿函数,你的仿函数的功能都不会发生变化
它既能像普通函数一样传入给定数量的参数(一个或者多个),还能存储我们需要的控制信息