在C/C++中,我们可以将函数当作普通的变量传递给另外一个函数以便定制函数的功能。比如实现排序功能的函数sort(),我们既可以选择从小到大的顺序也可以选择从大到小的顺序,只要传递给sort()不同的比较函数就可以了。不过,C和C++的做法不一样,前者采用函数指针,而后者采用函数对象。相比而言,C++的做法更值得推崇,它在性能上、状态维护上比C的做法灵活很多。
性能
对一个整型数组每个元素进行increment操作,分别用函数指针和函数对象的方法来实现,代码如下所示:
#include <stdio.h>
#include <stdlib.h>
template<class T>
void proc(int* s, int n, T f)
{
for(int i = 0; i < n; ++i)
{
f(*(s + i));
}
}
// 函数指针
void fInc(int& a)
{
++a;
}
// 函数对象
class Inc
{
public:
void operator()(int& a)
{
++a;
}
};
int main()
{
int ar[] = {0,1,2,3,4};
proc(ar,sizeof(ar)/sizeof(int),fInc);
// proc(ar,sizeof(ar)/sizeof(int),Inc());
for(int i = 0; i < sizeof(ar)/sizeof(int); ++i)
printf_s("%d ",ar[i]);
printf_s("\n");
system("pause");
}
函数对象之所以性能更高,是因为它本质上是class Inc的实例,proc()实际调用的是class Inc的成员方法,它缺省情况下是inline的。而函数指针必须经过thunk才能执行。
状态维护
函数对象善于维护函数的状态,因为class的成员变量可用来保存函数的状态。函数指针就只能借助于全局变量,灵活性、安全性都比不上函数对象。以下面的代码为例:
#include <iostream>
#include <algorithm>
#include <iterator>
//函数对象
class Countfrom
{
private:
int &count;
public:
Countfrom(int& n):count(n)
{
}
int operator()()
{
return count++;
}
};
// 函数指针
int s = 10;
int fCountfrom()
{
return s++;
}
int main()
{
int state(10);
int ar[11];
std::generate_n(ar,11,Countfrom(state));
// std::generate_n(ar,11,fCountfrom);
for(int i = 0; i < 11; ++i)
std::cout<<ar[i]<<std::endl;
system("pause");
}
从上面的例子就不难看出函数对象和函数指针在维护状态上的差别。函数指针必须要借助于全局变量才能维护状态,而函数对象直接用自己内部的count来维护,更加灵活、安全。