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

C++實(shí)現(xiàn)LeetCode(161.一個編輯距離)

C語言鏈表實(shí)現(xiàn)圖書管理系統(tǒng)