C++ 仿函數(shù)

仿函數(shù)

定義:仿函數(shù)(functor)鼎俘,就是使一個(gè)類的使用看上去像一個(gè)函數(shù)寥粹。其實(shí)現(xiàn)就是類中實(shí)現(xiàn)一個(gè)operator這個(gè)類就有了類似函數(shù)的行為渣锦,就是一個(gè)仿函數(shù)類了欧芽。

有時(shí)會(huì)發(fā)現(xiàn)有些功能實(shí)現(xiàn)的代碼男韧,會(huì)不斷的在不同的成員函數(shù)中用到朴摊,但是又不好將這些代碼獨(dú)立出來(lái)成為一個(gè)類的一個(gè)成員函數(shù)。但是又很想復(fù)用這些代碼此虑。

寫一個(gè)公共的函數(shù)甚纲,可以,這是一個(gè)解決方法朦前,不過(guò)函數(shù)用到的一些變量介杆,就可能成為公共的全局變量鹃操,再說(shuō)為了復(fù)用這么一片代碼,就要單立出一個(gè)函數(shù)春哨,也不是很好維護(hù)荆隘。

這時(shí)就可以用仿函數(shù)了,寫一個(gè)簡(jiǎn)單類赴背,除了那些維護(hù)一個(gè)[類的成員函數(shù)]外椰拒,就只是實(shí)現(xiàn)一個(gè)operator(),在類實(shí)例化時(shí)凰荚,就將要用的燃观,非參數(shù)的元素傳入類中。這樣就免去了對(duì)一些公共變量的全局化的維護(hù)了便瑟。又可以使那些代碼獨(dú)立出來(lái)缆毁,以便下次復(fù)用。

而且這些仿函數(shù)胳徽,還可以用關(guān)聯(lián)积锅,聚合,依賴的類之間的關(guān)系养盗,與用到他們的類組合在一起缚陷,這樣有利于資源的管理(這點(diǎn)可能是它相對(duì)于函數(shù)最顯著的優(yōu)點(diǎn)了)。如果在配合上模板技術(shù)和policy編程思想往核,那就更是威力無(wú)窮了

#include <iostream>
#include <set>
#include <algorithm>
using namespace std;

class CompareObject{
public:
    void operator()(){
        cout << "仿函數(shù)" << endl;
    }

    void operator()(int number,int number2){
        cout << "仿函數(shù)" << endl;
    }
};

// 查看c++ for_each源碼自定義
class ShowActionObj{
public:
    void operator()(int content){
        cout << "custom 仿函數(shù)" << content << endl;
    }
};
// 回調(diào)方式
void showAction(int content){
    cout << "custom 一元謂詞" << content << endl;

}

int main(){

    // 謂詞 == 仿函數(shù)

    CompareObject fun1;

    fun1();


    set<int> setVar;
    setVar.insert(10);
    setVar.insert(20);
    setVar.insert(30);
    setVar.insert(40);
    setVar.insert(50);
    setVar.insert(60);

    for_each(setVar.begin(),setVar.end(),ShowActionObj());
    cout << "---" << endl;
    for_each(setVar.begin(),setVar.end(),showAction);

    return 0;
}

再寫C++ STL 中總結(jié)了謂詞箫爷,相當(dāng)于仿函數(shù)
謂詞 <-> 仿函數(shù)(空謂詞 一元謂詞 二元謂詞 三元謂詞)

C#是通過(guò)委托delegate來(lái)實(shí)現(xiàn)仿函數(shù)的。
Java中的仿函數(shù)是通過(guò)實(shí)現(xiàn)包含單個(gè)函數(shù)的接口實(shí)現(xiàn)的
C語(yǔ)言使用[函數(shù)指針]和[回調(diào)函數(shù)]來(lái)實(shí)現(xiàn)仿函數(shù)聂儒,例如一個(gè)用來(lái)排序的函數(shù)可以這樣使用仿函數(shù)

List<String> list =Arrays.asList("10", "1", "20", "11", "21", "12");
Comparator<String> numStringComparator =new Comparator<String>(){
    publicint compare(String o1, String o2){
        returnInteger.valueOf(o1).compareTo(Integer.valueOf(o2));
    }
};
Collections.sort(list, numStringComparator);
#include <stdlib.h>
/* Callback function */
int compare_ints_function(void*A,void*B)
{
    return*((int*)(A))<*((int*)(B));
}
/* Declaration of C sorting function */
void sort(void*first_item,size_t item_size,void*last_item,int(*cmpfunc)(void*,void*));
int main(void)
{
    int items[]={4,3,1,2};
    sort((void*)(items),sizeof(int),(void*)(items +3), compare_ints_function);
    return 0;
}

回調(diào)函數(shù),謂詞虎锚,仿函數(shù) 分析

#include <iostream>
#include <set> // STL包
#include <algorithm> // 算法包

using namespace std;

// 我如何閱讀C++源碼,來(lái)寫我們的仿函數(shù)
// 明明白白的仿函數(shù)(一元謂詞==一元函數(shù)對(duì)象)
class showActionObj {
public:
    void operator()(int content) {
        cout << "自定義仿函數(shù)" << content << endl;
    }
};

// 回調(diào)函數(shù) 如果叫 仿函數(shù) 有點(diǎn)合理
// 簡(jiǎn)潔方式(回調(diào)函數(shù)衩婚、一元謂詞      但是不能稱為 仿函數(shù))
void showAction(int content) {
    cout << "自定義 一元謂詞" << content << endl;
}

using namespace std;

int main() {
    set<int> setVar;

    setVar.insert(10);
    setVar.insert(20);
    setVar.insert(30);
    setVar.insert(40);
    setVar.insert(50);
    setVar.insert(60);

    // for_each(setVar.begin(), setVar.end(), showActionObj());

    for_each(setVar.begin(), setVar.end(), showAction);

    return 0;
}

C++ 中窜护,STL + 算法包 + 迭代器 是分開(kāi)的。所以我們需要手動(dòng)組合非春。

image.png

for_each: 源碼


for_each.png
  • 回調(diào)函數(shù) (功能夠簡(jiǎn)單)
  • 仿函數(shù)(擴(kuò)展性強(qiáng)) C++內(nèi)置源碼使用仿函數(shù)頻率高柱徙,擴(kuò)展性強(qiáng)
#include <iostream>
#include <set> // STL包
#include <algorithm> // 算法包

using namespace std;

// 回調(diào)函數(shù) (功能夠簡(jiǎn)單)
void showAction(int __first) {
    cout << "一元謂詞" << __first << endl;
}

// 仿函數(shù)(擴(kuò)展性強(qiáng)) C++內(nèi)置源碼使用仿函數(shù)頻率高,擴(kuò)展性強(qiáng)
class showActionObj {
public:
    int count = 0;
    void _count() { cout << "本次輸出次數(shù)是:" << this->count << endl; }

    void operator() (int __first) {
        cout << "仿函數(shù)" << __first << endl;
        count++;
    }
};

int main() {
    // 理解:類型傳遞
    // set<int, showActionObj> setVar; 這樣寫的語(yǔ)法是OK的奇昙,不能加括號(hào)
    set<int> setVar;

    setVar.insert(10);
    setVar.insert(20);
    setVar.insert(30);
    setVar.insert(40);
    setVar.insert(50);
    setVar.insert(60);

    // TODO 第一種方式
    for_each(setVar.begin(), setVar.end(), showAction);
    // 請(qǐng)你統(tǒng)計(jì)打印次數(shù)护侮? 答:做不到

    // TODO 第二種方式
    showActionObj s; // 理解:值傳遞
    s = for_each(setVar.begin(), setVar.end(), s); // 傳入進(jìn)去的s是新的副本,我們外面的s是舊地址
    // 請(qǐng)你統(tǒng)計(jì)打印次數(shù)储耐? 答:OK
    s._count();

    return 0;
}

看下_Function


image.png

可以解釋這句話

s = for_each(setVar.begin(), setVar.end(), s); // 傳入進(jìn)去的s是新的副本羊初,我們外面的s是舊地址

仿函數(shù) 能做到的封裝,回調(diào)函數(shù)是做不到的什湘。源碼用到了大量仿函數(shù)长赞,證明它的擴(kuò)展性好晦攒。

解決 賦值問(wèn)題說(shuō)明

類型傳遞仿函數(shù) 怎么看源碼得知寫法

set<string> setVar; 

點(diǎn)擊進(jìn)入源碼查看

看到這部分模板定義

template <class _Key, class _Compare = less<_Key>,
          class _Allocator = allocator<_Key> >

第一個(gè)參數(shù)就是我們<> 里面的類型,第二個(gè)參數(shù)得哆,可以不寫勤家,默認(rèn)參數(shù)less
less ,public繼承了binary_function

struct _LIBCPP_TEMPLATE_VIS less : binary_function<_Tp, _Tp, bool>
{
    _LIBCPP_CONSTEXPR_AFTER_CXX11 _LIBCPP_INLINE_VISIBILITY
    bool operator()(const _Tp& __x, const _Tp& __y) const
        {return __x < __y;}
};

這樣做,思想和RxJava一致柳恐,做到統(tǒng)一過(guò)濾用,有成百上千的操作符热幔。
父類就可以得到我的子類仿函數(shù)乐设,根據(jù)這個(gè),我們就可以自定義仿函數(shù)绎巨。仿造源碼近尚,寫一個(gè)二元謂詞

#include <iostream>
#include <set>
using namespace std;

// C++源碼:typename _Compare = std::less   less內(nèi)置的仿函數(shù),根據(jù)內(nèi)置仿函數(shù)去寫 自定義
//  bool operator()(const _Tp& __x, const _Tp& __y) const 二元謂詞
class CompareObjectClass {
public:
    bool operator() (const string & __x, const string & __y) const { // const 指針 const  常量指針常量 = 只讀
        return __x > __y;
    }
};

int main() {
    set<string, CompareObjectClass> setVar; // 第一版
    setVar.insert(setVar.begin(), "AAAAAAA");
    setVar.insert(setVar.begin(), "BBBBBBB");
    setVar.insert(setVar.begin(), "CCCCCCC");
    setVar.insert(setVar.begin(), "DDDDDDD");
    setVar.insert(setVar.begin(), "EEEEEEE");
    setVar.insert(setVar.begin(), "FFFFFFF");
    // 迭代器 循環(huán)
    for (set<string>::iterator iteratorVar = setVar.begin(); iteratorVar != setVar.end(); iteratorVar++) {
        cout << "循環(huán)item:" << *iteratorVar  << "\t";
        // 循環(huán)item:AAAAAAA   循環(huán)item:BBBBBBB  循環(huán)item:CCCCCCC  循環(huán)item:DDDDDDD  循環(huán)item:EEEEEEE  循環(huán)item:FFFFFFF

        // 循環(huán)item:FFFFFFF   循環(huán)item:EEEEEEE  循環(huán)item:DDDDDDD  循環(huán)item:CCCCCCC  循環(huán)item:BBBBBBB  循環(huán)item:AAAAAAA
    }
    return 0;
}

通過(guò)Set 的內(nèi)置反函數(shù)less场勤, 內(nèi)置反函數(shù)戈锻,定義我們自己的函數(shù)

容器存儲(chǔ)對(duì)象,生命周期

set 存入對(duì)象 奔潰(set會(huì)自動(dòng)排序和媳,對(duì)象沒(méi)法排序格遭,所以奔潰) 解決方案:自定義仿函數(shù)解決

為了方便避免重復(fù),我們用vector存儲(chǔ)對(duì)象留瞳,說(shuō)明生命周期

#include <iostream>
#include <set> // set 存入對(duì)象 奔潰(set會(huì)自動(dòng)排序拒迅,對(duì)象沒(méi)法排序,所以奔潰)  解決方案:自定義仿函數(shù)解決
#include <vector>  // 存入對(duì)象

using namespace std;

class Person {
private:
    string name;
public:
    Person(string name) : name(name) {}

    void setName(string name) {
        this->name = name;
    }

    string getName() {
        return this->name;
    }

    Person(const Person &person) {
        this->name = person.name; // 淺拷貝

        cout << "Person拷貝構(gòu)造函數(shù)執(zhí)行了..." << endl;
    }

    ~Person() {
        cout << "Person析構(gòu)函數(shù)執(zhí)行了" << endl;
    }
};

int main() {
    // Java:把對(duì)象存入 添加到 集合
    // C++: 調(diào)用拷貝構(gòu)造函數(shù)她倘,存進(jìn)去的是另一個(gè)新的對(duì)象

    vector<Person> vectorVar;

    // person 被main函數(shù)彈棧 析構(gòu)一次
    Person person("David"); // 2  David

    // 里面的insert函數(shù)彈棧 析構(gòu)一次
    vectorVar.insert(vectorVar.begin(), person); // 外面的person是舊地址璧微,到insert函數(shù)里面的person就是新地址(拷貝構(gòu)造函數(shù) 一次)

    person.setName("Kevin"); // 1

    // newPerson 被main函數(shù)彈棧 析構(gòu)一次
    Person newPerson =
            vectorVar.front(); // front里面的person是舊地址, 外面的newPerson就是新地址(拷貝構(gòu)造函數(shù) 一次)

    cout << "newPerson:" << newPerson.getName().c_str() << endl;

    // 3次析構(gòu)函數(shù)   兩次拷貝構(gòu)造

    return 0;
} // main彈棧

代碼執(zhí)行了硬梁,3次析構(gòu)函數(shù) 兩次拷貝構(gòu)造

Person person("David")

Person newPerson =vectorVar.front()

執(zhí)行拷貝構(gòu)造函數(shù) front里面的是舊的地址前硫,外面的newPerson 就是新的地址

Person person("David")

main函數(shù)執(zhí)行完會(huì)析構(gòu)一次

vectorVar.insert(vectorVar.begin(), person) i

nsert 內(nèi)部結(jié)束也會(huì)析構(gòu)一次

Person newPerson =vectorVar.front()

newPerson 被main函數(shù)彈棧 析構(gòu)一次

預(yù)定義函數(shù)

C++ 內(nèi)置函數(shù)

#include <iostream>
#include <set> // STL包
#include <algorithm> // 算法包
using namespace std;

int main() {
    // "David" + "AAAA" // 運(yùn)算符重載

    // C++已經(jīng)提供了 預(yù)定義函數(shù)  plus,minus,multiplies,divides,modulus ...
    plus<int> add_func;

    int r = add_func(1, 1);
    cout << r << endl;

    plus<string> add_func2;
    string r2 = add_func2("AAAA", "BBB");
    cout << r2 << endl;

    plus<float> add_func3;
    float r3 = add_func3(4354.45f, 34.3f);
    cout << r3 << endl;

    return 0;
}

簡(jiǎn)單的例子,plus 荧止。 我們拼接字符串可能想到運(yùn)算符重載屹电,但是c++內(nèi)置plus解決了這個(gè)問(wèn)題。

有些比如罩息,對(duì)象可能就不適用嗤详,我們可以手寫預(yù)定義函數(shù)
: public binary_function_t<T, T, T>
可以要可以不要

#include <iostream>
#include <set> // STL包
#include <algorithm> // 算法包
using namespace std;

template<typename Arg1, typename Arg2, typename Result>
struct binary_function_t
{
    /// 第一個(gè)參數(shù)類型 是底一個(gè)參數(shù)的類型
    typedef Arg1    first_argument_type;

    //econd_argument_type是第二個(gè)參數(shù)的類型
    typedef Arg2    second_argument_type;

    /// @c result_type是返回類型
    typedef Result  result_type;
};

// TODO 對(duì)象 + 對(duì)象
// 1.運(yùn)算符重載
// 2.對(duì)象+對(duì)象 自己去寫仿函數(shù)

template<typename T>
struct plus_d : public binary_function_t<T, T, T>
{
    T operator() (const T & x, const T & y) {
        return x + y;
    }
};

int main() {

    plus_d<int> add_func;
    int r = add_func(1, 1);
    cout << r << endl;

    plus_d<string> add_func2;
    string r2 = add_func2("AAAA", "BBB");
    cout << r2 << endl;

    plus_d<float> add_func3;
    float r3 = add_func3(4354.45f, 34.3f);
    cout << r3 << endl;

    return 0;
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市瓷炮,隨后出現(xiàn)的幾起案子葱色,更是在濱河造成了極大的恐慌,老刑警劉巖娘香,帶你破解...
    沈念sama閱讀 206,839評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件苍狰,死亡現(xiàn)場(chǎng)離奇詭異办龄,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)淋昭,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,543評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門俐填,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人翔忽,你說(shuō)我怎么就攤上這事英融。” “怎么了歇式?”我有些...
    開(kāi)封第一講書(shū)人閱讀 153,116評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵驶悟,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我材失,道長(zhǎng)痕鳍,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 55,371評(píng)論 1 279
  • 正文 為了忘掉前任龙巨,我火速辦了婚禮笼呆,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘旨别。我一直安慰自己诗赌,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,384評(píng)論 5 374
  • 文/花漫 我一把揭開(kāi)白布秸弛。 她就那樣靜靜地躺著境肾,像睡著了一般。 火紅的嫁衣襯著肌膚如雪胆屿。 梳的紋絲不亂的頭發(fā)上奥喻,一...
    開(kāi)封第一講書(shū)人閱讀 49,111評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音非迹,去河邊找鬼环鲤。 笑死,一個(gè)胖子當(dāng)著我的面吹牛憎兽,可吹牛的內(nèi)容都是我干的冷离。 我是一名探鬼主播,決...
    沈念sama閱讀 38,416評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼纯命,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼西剥!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起亿汞,我...
    開(kāi)封第一講書(shū)人閱讀 37,053評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤瞭空,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體咆畏,經(jīng)...
    沈念sama閱讀 43,558評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡南捂,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,007評(píng)論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了旧找。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片溺健。...
    茶點(diǎn)故事閱讀 38,117評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖钮蛛,靈堂內(nèi)的尸體忽然破棺而出鞭缭,到底是詐尸還是另有隱情,我是刑警寧澤魏颓,帶...
    沈念sama閱讀 33,756評(píng)論 4 324
  • 正文 年R本政府宣布缚去,位于F島的核電站,受9級(jí)特大地震影響琼开,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜枕荞,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,324評(píng)論 3 307
  • 文/蒙蒙 一柜候、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧躏精,春花似錦渣刷、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,315評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至瞭吃,卻和暖如春碌嘀,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背歪架。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,539評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工股冗, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人和蚪。 一個(gè)月前我還...
    沈念sama閱讀 45,578評(píng)論 2 355
  • 正文 我出身青樓止状,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親攒霹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子怯疤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,877評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容