在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來維護,更加靈活、安全。