對(duì)于 c++中仿函數(shù)的理解
先考慮一個(gè)簡(jiǎn)單的例子:假設(shè)有一個(gè)vector,你的任務(wù)是統(tǒng)計(jì)長(zhǎng)度小于5的string的個(gè)數(shù),如果使用count_if函數(shù)的話,你的代碼可能長(zhǎng)成這樣:
1?bool?LengthIsLessThanFive(const?string&?str)?{
2??????return?str.length()<5;????
3?}
4?int?res=count_if(vec.begin(),?vec.end(),?LengthIsLessThanFive);1234
其中count_if函數(shù)的第三個(gè)參數(shù)是一個(gè)函數(shù)指針,返回一個(gè)bool類型的值。一般的,如果需要將特定的閾值長(zhǎng)度也傳入的話,我們可能將函數(shù)寫成這樣:
1?bool?LenthIsLessThan(const?string&?str,?int?len)?{
2?????return?str.length()<len;
3?}123
這個(gè)函數(shù)看起來(lái)比前面一個(gè)版本更具有一般性,但是他不能滿足count_if函數(shù)的參數(shù)要求:count_if要求的是unary function(僅帶有一個(gè)參數(shù))作為它的最后一個(gè)參數(shù)。所以問(wèn)題來(lái)了,怎么樣找到以上兩個(gè)函數(shù)的一個(gè)折中的解決方案呢?
這個(gè)問(wèn)題其實(shí)可以歸結(jié)于一個(gè)data flow的問(wèn)題,要設(shè)計(jì)這樣一個(gè)函數(shù),使其能夠access這個(gè)特定的length值,回顧我們已有的知識(shí),有2種解決方案可以考慮:
1、函數(shù)的參數(shù);
這種方法我們已經(jīng)討論過(guò)了,多個(gè)參數(shù)不適用于count_if函數(shù)。
2、全局變量;
我們可以將長(zhǎng)度閾值設(shè)置成一個(gè)全局變量,代碼可能像這樣:
1?int?maxLength;
2?bool?LengthIsLessThan(const?string&?str)?{
3?????return?str.length()<maxLength;
4?}
5?int?res=count_if(vec.begiin(),?vec.end(),?LengthIsLessThan);12345
這段代碼看似很不錯(cuò),實(shí)則不符合規(guī)范,剛重要的是,它不優(yōu)雅。原因有以下幾點(diǎn)要考慮:
1、容易出錯(cuò);
為什么這么說(shuō)呢,我們必須先初始化maxLength的值,才能繼續(xù)接下來(lái)的工作,如果我們忘了,則可能無(wú)法得到正確答案。此外,變量maxLength和函數(shù)LengthIsLessThan之間是沒(méi)有必然聯(lián)系的,編譯器無(wú)法確定在調(diào)用該函數(shù)前是否將變量初始化,給碼農(nóng)平添負(fù)擔(dān)。
2、沒(méi)有可擴(kuò)展性;
如果我們每遇到一個(gè)類似的問(wèn)題就新建一個(gè)全局變量,尤其是多人合作寫代碼時(shí),很容易引起命名空間污染(namespace polution)的問(wèn)題;當(dāng)范圍域內(nèi)有多個(gè)變量時(shí),我們用到的可能不是我們想要的那個(gè)。
3、全局變量的問(wèn)題;
每當(dāng)新建一個(gè)全局變量,即使是為了coding的便利,我們也要知道我們應(yīng)該盡可能的少使用全局變量,因?yàn)樗腸ost很高;而且可能暗示你這里有一些待解決的優(yōu)化方案。
說(shuō)了這么多,還是要回到我們?cè)嫉哪莻€(gè)問(wèn)題,有什么解決方案呢?答案當(dāng)然就是這篇blog的正題部分:仿函數(shù)。
我們的初衷是想設(shè)計(jì)一個(gè)unary function,使其能做binary function的工作,這看起來(lái)并不容易,但是仿函數(shù)能解決這個(gè)問(wèn)題。
先來(lái)看仿函數(shù)的通俗定義:仿函數(shù)(functor)又稱為函數(shù)對(duì)象(function object)是一個(gè)能行使函數(shù)功能的類。仿函數(shù)的語(yǔ)法幾乎和我們普通的函數(shù)調(diào)用一樣,不過(guò)作為仿函數(shù)的類,都必須重載operator()運(yùn)算符,舉個(gè)例子:
1?class?Func{
2?????public:
3?????????void?operator()?(const?string&?str)?const?{
4?????????????cout<<str<>>helloworld!123456789
仿函數(shù)其實(shí)是上述解決方案中的第四種方案:成員變量。成員函數(shù)可以很自然的訪問(wèn)成員變量:
?1?class?StringAppend{
?2?????public:
?3?????????explicit?StringAppend(const?string&?str)?:?ss(str){}
?4?
?5?????????void?operator()?(const?string&?str)?const{
?6??????????????cout<<str<<'?'<<ss<>>hellois?world123456789101112131415
我相信這個(gè)例子能讓你體會(huì)到一點(diǎn)點(diǎn)仿函數(shù)的作用了;它既能想普通函數(shù)一樣傳入給定數(shù)量的參數(shù),還能存儲(chǔ)或者處理更多我們需要的有用信息。
讓我們回到count_if的問(wèn)題中去,是不是覺(jué)得問(wèn)題變得豁然開(kāi)朗了?
1?class?ShorterThan?{
2?????public:
3?????????explicit?ShorterThan(int?maxLength)?:?length(maxLength)?{}
4?????????bool?operator()?(const?string&?str)?const?{
5?????????????return?str.length()?<?length;
6?????????}
7?????private:
8?????????const?int?length;
9?};123456789
1?count_if(myVector.begin(),?myVector.end(),?ShorterThan(length));//直接調(diào)用即可
1
這里需要注意的是,不要糾結(jié)于語(yǔ)法問(wèn)題:ShorterThan(length)似乎并沒(méi)有調(diào)用operator()函數(shù)?其實(shí)它調(diào)用了,創(chuàng)建了一個(gè)臨時(shí)對(duì)象。你也可以自己加一些輸出語(yǔ)句看一看。
這篇博文就先記到這里了,仿函數(shù)也在STL中大量涉及到,不徹底弄懂仿函數(shù)的問(wèn)題看到STL源碼就會(huì)一頭包。后續(xù)可能再分享一些關(guān)于functor的資料和個(gè)人學(xué)習(xí)心得。
轉(zhuǎn)發(fā)原因:這個(gè)用例對(duì)仿函數(shù)的用處解釋的比較清楚,其實(shí)相比用函數(shù)指針去處理,仿函數(shù)多了很多的靈活性,而且STL中也預(yù)先實(shí)現(xiàn)了一些仿函數(shù),需要包頭文件#include
===========2017.9.18補(bǔ)充=============?
在c++11里面可以通過(guò)lambda表達(dá)式解決上述問(wèn)題:
#include#include#include#includeusing?namespace?std;
int?main()
{
????std::vectorc{?1,?5,?3,?4,?5,?6,?7?};
????int?x?=?5;
????int?k=std::count_if(c.begin(),?c.end(),?[x](int?n){return?x?==?n;?});
????cout?<<?k?<<?endl;
????return?0;
}12345678910111213
比仿函數(shù)方便多了:)





