函数对象(Function Object)又称函数对象类、仿函数、高阶函数等,函数对象实际上是指那些可以被传入其他函数或是从其他函数返回的函数(比如std::for_each函数的第3个参数就要求传入接受一个参数的函数或函数对象),是不是和函数指针的作用很相似,后面会介绍函数对象和函数指针的差别~
感觉上面关于函数对象的定义还是好复杂呀,通俗的讲函数对象就是一个重载了operator()操作符的类,这就是函数对象和普通类的差别,也就是说重载了operator()操作符的类都可以叫函数对象类,由于重载了operator()操作符的类有和普通函数类似的使用方法(但是函数对象使用前需要定义一个对象,准确来说应该和函数指针更相似,函数指针在使用前也需要typedef定义一个类型),故称函数对象、函数对象类、仿函数等等。下面来看一个简单的函数对象及其使用:
#include <iostream>
//普通函数
int add(int a, int b)
{
return a + b;
}
//函数对象类
class FunctionObjectDefine
{
public:
int operator()(int a, int b) { return a + b; };
};
int main()
{
FunctionObjectDefine func;
int total = add(1, 2);
int sum = func(1, 2); //是不是和普通函数add(1, 2)调用方式一样
std::cout << "Total:" << total << std::endl;
std::cout << "Sum:" << sum << std::endl;
return 0;
}
函数对象和函数指针
前面提到函数对象和函数指针的作用很相似,下面先看一个小例子:
#include <iostream>
int add_func(int a, int b)
{
return a + b;
}
//函数对象
class AddClass
{
public:
int operator()(int a, int b) { return a + b; }
};
//函数指针
typedef int (*AddFunction)(int a, int b);
int main()
{
//为了方便对比 将函数对象和函数指针声明了同名变量
//为了防止报错 故两个同名变量都有各自的作用域 每个的作用域在各自的{}中
//通过后面的调用发现两者调用方式是一样的 都是通过add(1, 2)调用
{
//函数对象的使用
AddClass add;
int sum = add(1, 2);
std::cout << "Sum:" << sum << std::endl;
}
{
//函数指针的使用
AddFunction add = &add_func;
int sum = add(2, 2);
std::cout << "Sum:" << sum << std::endl;
}
return 0;
}
通过上面的小例子我们很容易发现,函数对象和函数指针在定义的方式不一样,但是调用的方式是一样的。既然已经有了函数指针这个东西,为什么还要发明函数对象了,其实很简单,函数对象可以将附加数据保存在成员变量中,从而实现携带附加数据,而函数指针就不行了。考虑下面一个应用场景,我们需要使用std::for_ecah
将一个std::vector
中的每一个值加上某个值然后输出,如果使用普通函数,则其声明应该为void add_num(int value, int num)
,其中value为容器中的元素,num为要加上的数。但是由于std::for_each
函数的第3个参数就要求传入接受一个参数的函数或函数对象,所以将add_num
函数传入std::for_each
是错误的,然而函数对象可以携带附加数据解决这个问题,看下面的例子:
#include <iostream>
#include <vector>
#include <algorithm>
class Add
{
public:
Add(int num) : num_(num)
{ }
void operator()(int value)
{ std::cout << value + num_ << std::endl; }
private:
int num_;
};
int main()
{
std::vector<int> vecInt;
vecInt.push_back(1);
vecInt.push_back(2);
vecInt.push_back(3);
Add add(2);
//std::for_each的第3个对象为函数对象
std::for_each(vecInt.begin(), vecInt.end(), add);
}