函数对象又叫伪函数.函数对象本来是一个类,但由于基本上没干啥事,就只封装一个函数在里面,并且被封装的函数还是通过重载运算符()来实现.
可能你很是纳闷,为啥搞个这东东出来啊?
其实是这样的,刚开始一些简单应用完全用个函数完全可以搞定,传个函数指针就行了.但这样不够灵活,可扩展性不好.于是干脆整个类出来得了.
函数指针的缺陷
还是举个例子来瞧瞧吧~
如果我们有个工厂要招人,然后要符合很多标准才行.本着分工合作的原则,那个判断符合条件的工作完全由另一个函数来完成.(当然这只是为了举例方便,有点牵强的)
假如函数IsOK(int userID)来判断符合条件不.
bool IsOK(int userID)
{
//假如通过userID在数据库查找各种信息,然后判断
int age = GetAge(userID); //这个函数的实现就省略了,其他很多判断信息也不管了
if( age > 18)
return true;
else
return false;
}
void PrintAllLaborName(int userID, string name , bool (*pFun)(int)) //第二个参数是个函数指针
{
if( pFun(userID) ) //符合条件则打印姓名
cout<<name<<endl;
}
如果是简单的应用,就像上面一样在PrintAllLaborName中传个函数指针就行了,但啥时突然要变成需要其他一些条件,函数的参数要变.这样改起来就不太方便.
假如IsOK(int )要变成 IsOk(int , string) ,这样就得有两处要改了.一个是IsOK函数本身,另一个就是PrintAllLaborName的参数.
那我们知道面向对象类的好处就是封装,对外提供接口就行了.要改只改类里面的,接口不变.于是我们就想着把函数IsOK封装到一个类里面去.
函数对象
template<class T>
struct CheckCondition //这就是一个函数对象,只要重载运算符()
{
bool operator() (int userID , T otherInfo) //假如有很多信息要传,到时定义一个类然后用T传过来就行的
{
// if( Check( userID , otherInfo) ) 这里就不实现该功能了
return true;
//else
//return false;
}
}
template<class T>
void PrintAllLaborName(int userID, string name , T fun) //第二个参数是个函数对象
{
if( fun(userID, name ) //符合条件则打印姓名
cout<<name<<endl;
}
测试代码
PrintAllLaborName ( 123, "arwen" , CheckCondition<string>() );
函数对象传函数时的迷惑
看完例子你可能有两个疑问,
一个是重载运算符()的好处,
另一个是函数对象用起来有点怪怪的.都没有看到那个类在哪里实例化
重载()的好处是它的参数不受限制的,所以你可以模仿任意函数.而像其他运算符(一元运算符,二元运算符)只能有一个或两个参数.
函数对象的实例化:
首先是函数的形参是个类,然后里面调用,这好理解
template<class T>
void PrintAllLaborName(int userID, string name , T fun)
然后里面调用fun(userID, name )
但这里就不明白了
PrintAllLaborName ( 123, "arwen" , CheckCondition<string>() );
CheckCondition<string>()这个参数也太怪了吧.
如果是CheckCondition<string> checkCon;
然后PrintAllLaborName ( 123, "arwen" , checkCon);这样就非常好理解了.
实例化成一个对象,然后传过去就好了嘛.实际上这样是完全可以的,一点也没错.
不过大多数时候我们却没这样用,而是CheckCondition<string>()这样用,为啥? 因为这样有这么个好处.
我们知道CheckCondition<string> checkCon;这样实例化一个对象后,等你调用完了函数PrintAllLaborName该对象仍然没出作用域,没有被析构掉.
而CheckCondition<string>()就是调用构造函数,这样是传一个临时对象过去,一调用完函数就被析构(调用完立马被析构,这就是好处)
PrintAllLaborName ( 123, "arwen" , CheckCondition<string>() )这样传个参数过去,发生的动作是
void PrintAllLaborName(int userID, string name , T fun)
{
//下面两步操作是后台实现的
CheckCondition<string> tmp ;
fun = tmp;
}