淺析C++ 仿函數(shù)
1.為什么要有仿函數(shù)
我們先從一個(gè)非常簡(jiǎn)單的問(wèn)題入手。假設(shè)我們現(xiàn)在有一個(gè)數(shù)組,數(shù)組中存有任意數(shù)量的數(shù)字,我們希望能夠計(jì)數(shù)出這個(gè)數(shù)組中大于10的數(shù)字的數(shù)量,你的代碼很可能是這樣的:
#include <iostream>
using namespace std;
int RecallFunc(int *start, int *end, bool (*pf)(int))
{
int count=0;
for(int *i=start;i!=end+1;i++)
{
count = pf(*i) ? count+1 : count;
}
return count;
}
bool IsGreaterThanTen(int num)
{
return num>10 ? true : false;
}
int main()
{
int a[5] = {10,100,11,5,19};
int result = RecallFunc(a,a+4,IsGreaterThanTen);
cout<<result<<endl;
return 0;
}
RecallFunc()函數(shù)的第三個(gè)參數(shù)是一個(gè)函數(shù)指針,用于外部調(diào)用,而IsGreaterThanTen()函數(shù)通常也是外部已經(jīng)定義好的,它只接受一個(gè)參數(shù)的函數(shù)。如果此時(shí)希望將判定的閾值也作為一個(gè)變量傳入,變?yōu)槿缦潞瘮?shù)就不可行了:
bool IsGreaterThanThreshold(int num, int threshold)
{
return num>threshold ? true : false;
}
雖然這個(gè)函數(shù)看起來(lái)比前面一個(gè)版本更具有一般性,但是它不能滿(mǎn)足已經(jīng)定義好的函數(shù)指針參數(shù)的要求,因?yàn)楹瘮?shù)指針參數(shù)的類(lèi)型是bool (*)(int),與函數(shù)bool IsGreaterThanThreshold(int num, int threshold)的類(lèi)型不相符。如果一定要完成這個(gè)任務(wù),按照以往的經(jīng)驗(yàn),我們可以考慮如下可能途徑:
(1)閾值作為函數(shù)的局部變量。局部變量不能在函數(shù)調(diào)用中傳遞,故不可行;
(2)函數(shù)傳參。這種方法我們已經(jīng)討論過(guò)了,多個(gè)參數(shù)不適用于已定義好的RecallFunc函數(shù)。
(3)全局變量。我們可以將閾值設(shè)置成一個(gè)全局變量。這種方法雖然可行,但是不優(yōu)雅,且非常容易引入Bug,比如全局變量容易同名,造成命名空間污染。
那么有什么好的處理方法呢?仿函數(shù)應(yīng)運(yùn)而生。
2.仿函數(shù)的定義
仿函數(shù)(Functor)又稱(chēng)為函數(shù)對(duì)象(Function Object)是一個(gè)能行使函數(shù)功能的類(lèi)。仿函數(shù)的語(yǔ)法幾乎和我們普通的函數(shù)調(diào)用一樣,不過(guò)作為仿函數(shù)的類(lèi),都必須重載operator()運(yùn)算符。因?yàn)檎{(diào)用仿函數(shù),實(shí)際上就是通過(guò)類(lèi)對(duì)象調(diào)用重載后的operator()運(yùn)算符。
如果編程者要將某種“操作”當(dāng)做算法的參數(shù),一般有兩種方法:
(1)一個(gè)辦法就是先將該“操作”設(shè)計(jì)為一個(gè)函數(shù),再將函數(shù)指針當(dāng)做算法的一個(gè)參數(shù)。上面的實(shí)例就是該做法;
(2)將該“操作”設(shè)計(jì)為一個(gè)仿函數(shù)(就語(yǔ)言層面而言是個(gè)class),再以該仿函數(shù)產(chǎn)生一個(gè)對(duì)象,并以此對(duì)象作為算法的一個(gè)參數(shù)。
很明顯第二種方法會(huì)更優(yōu)秀,原因也在上一小節(jié)有所闡述。正如上面的例子,在我們寫(xiě)代碼時(shí)有時(shí)會(huì)發(fā)現(xiàn)有些功能代碼,會(huì)不斷地被使用。為了復(fù)用這些代碼,實(shí)現(xiàn)為一個(gè)公共的函數(shù)是一個(gè)解決方法。不過(guò)函數(shù)用到的一些變量,可能是公共的全局變量。引入全局變量,容易出現(xiàn)同名沖突,不方便維護(hù)。
這時(shí)就可以用仿函數(shù)了,寫(xiě)一個(gè)簡(jiǎn)單類(lèi),除了維護(hù)類(lèi)的基本成員函數(shù)外,只需要重載operator()運(yùn)算符 。這樣既可以免去對(duì)一些公共變量的維護(hù),也可以使重復(fù)使用的代碼獨(dú)立出來(lái),以便下次復(fù)用。而且相對(duì)于函數(shù)更優(yōu)秀的性質(zhì),仿函數(shù),還可以進(jìn)行依賴(lài)、組合與繼承等,這樣有利于資源的管理。如果再配合模板技術(shù)和Policy編程思想,那就更是威力無(wú)窮了,大家可以慢慢體會(huì)。Policy表述了泛型函數(shù)和泛型類(lèi)的一些可配置行為(通常都具有被經(jīng)常使用的缺省值)。
STL中也大量涉及到仿函數(shù),有時(shí)仿函數(shù)的使用是為了函數(shù)擁有類(lèi)的性質(zhì),以達(dá)到安全傳遞函數(shù)指針、依據(jù)函數(shù)生成對(duì)象、甚至是讓函數(shù)之間有繼承關(guān)系、對(duì)函數(shù)進(jìn)行運(yùn)算和操作的效果。比如STL中的容器set就使用了仿函數(shù)less ,而less繼承的binary_function,就可以看作是對(duì)于一類(lèi)函數(shù)的總體聲明了,這是函數(shù)做不到的。
//less的定義
template<typename _Tp> struct less : public binary_function<_Tp, _Tp, bool>
{
bool operator()(const _Tp& __x, const _Tp& __y) const
{ return __x < __y; }
};
//set的申明
template<typename _Key,
typename _Compare = std::less<_Key>,
typename _Alloc = std::allocator<_Key>>
class set;
仿函數(shù)中的變量可以是static的,同時(shí)仿函數(shù)還給出了static的替代方案,仿函數(shù)內(nèi)的靜態(tài)變量可以改成類(lèi)的私有成員,這樣可以明確地在析構(gòu)函數(shù)中清除所用的內(nèi)容,如果用到了指針,那么這個(gè)是不錯(cuò)的選擇。有人說(shuō)這樣的類(lèi)已經(jīng)不是仿函數(shù)了,但其實(shí),封裝后從外界觀察,可以明顯地發(fā)現(xiàn),它依然有函數(shù)的性質(zhì)。
3.仿函數(shù)實(shí)例
我們先來(lái)看一個(gè)仿函數(shù)的例子:
#include <iostream>
#include <string>
using namespace std;
class Functor
{
public:
void operator() (const string& str) const
{
cout << str << endl;
}
};
int main()
{
Functor myFunctor;
myFunctor("Hello world!");
return 0;
}
程序輸出:
Hello world!。
可以見(jiàn)到,仿函數(shù)提供了第四種解決方案:成員變量。成員函數(shù)可以很自然的訪問(wèn)成員變量,從而解決上文最開(kāi)始的那個(gè)問(wèn)題。
class StringAppend
{
public:
explicit StringAppend(const string& str) : ss(str){}
void operator() (const string& str) const
{
cout<<str<<' '<<ss<<endl;
}
private:
const string ss;
};
int main()
{
StringAppend myFunctor2("and world!");
myFunctor2("Hello");
return 0;
}
程序輸出:
Hello and world!。
這個(gè)例子應(yīng)該可以讓您體會(huì)到仿函數(shù)的一些作用:它既能像普通函數(shù)一樣傳入給定數(shù)量的參數(shù),還能存儲(chǔ)或者處理更多我們需要的有用信息。于是本小節(jié)開(kāi)頭的問(wèn)題就迎刃而解了:
#include <iostream>
using namespace std;
class IsGreaterThanThresholdFunctor
{
public:
explicit IsLessThanTenFunctor(int tmp_threshold) : threshold(tmp_threshold{}
bool operator() (int num) const
{
return num>10 ? true : false;
}
private:
const int threshold;
};
int RecallFunc(int *start, int *end, IsGreaterThanThresholdFunctor myFunctor)
{
int count=0;
for(int *i=start;i!=end+1;i++)
{
count = myFunctor(*i) ? count+1 : count;
}
return count;
}
int main()
{
int a[5] = {10,100,11,5,19};
int result = RecallFunc(a,a+4,IsLessThanTenFunctor(10));
cout<<result<<endl;
return 0;
}
以上就是淺析C++ 仿函數(shù)的詳細(xì)內(nèi)容,更多關(guān)于C++ 仿函數(shù)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
c語(yǔ)言大小端(數(shù)據(jù)在內(nèi)存中的存儲(chǔ))
大小端是內(nèi)存存儲(chǔ)字節(jié)的兩種方式,一個(gè)是大端存儲(chǔ),一個(gè)是小端存儲(chǔ),本文主要介紹了c語(yǔ)言大小端,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09
C/C++中的mem函數(shù)和strcopy函數(shù)的區(qū)別和應(yīng)用
strcpy和memcpy都是標(biāo)準(zhǔn)C庫(kù)函數(shù),strcpy提供了字符串的復(fù)制而memcpy提供了一般內(nèi)存的復(fù)制。下面通過(guò)本文重點(diǎn)給大家介紹C/C++中的mem函數(shù)和strcopy函數(shù)的區(qū)別和應(yīng)用,非常不錯(cuò),感興趣的朋友一起看下吧2016-08-08
C語(yǔ)言實(shí)現(xiàn)貪吃蛇小游戲開(kāi)發(fā)
這篇文章主要為大家詳細(xì)介紹了C語(yǔ)言實(shí)現(xiàn)貪吃蛇小游戲開(kāi)發(fā),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08
C++實(shí)現(xiàn)LeetCode(161.一個(gè)編輯距離)
C語(yǔ)言鏈表實(shí)現(xiàn)圖書(shū)管理系統(tǒng)
C語(yǔ)言完數(shù)的實(shí)現(xiàn)示例

