使用std::function作為函數(shù)入?yún)?/h1>

1. 關(guān)于std::function()

在C語言的時代具则,我們可以使用函數(shù)指針來吧一個函數(shù)作為參數(shù)傳遞砾淌,這樣我們就可以實(shí)現(xiàn)回調(diào)函數(shù)的機(jī)制啦撮。到了C++11以后在標(biāo)準(zhǔn)庫里引入了std::function模板類,這個模板概括了函數(shù)指針的概念
函數(shù)指針只能指向一個函數(shù)汪厨,而std::function對象可以代表任何可以調(diào)用的對象赃春,比如說任何可以被當(dāng)作函數(shù)一樣調(diào)用的對象。
當(dāng)你創(chuàng)建一個函數(shù)指針的時候骄崩,你必須定義這個函數(shù)簽名(表征這個函數(shù)的入?yún)⑵噶郏祷刂档刃畔ⅲ┍「ǎ煌瑯拥模?dāng)你創(chuàng)建一個std::function對象的時候抠璃,你也必須指定它所代表的可調(diào)用對象的函數(shù)簽名站楚。這一點(diǎn)可以通過std::function的模板參數(shù)來實(shí)現(xiàn)。
舉個例子來說搏嗡,如果要定義一個std::function對象func窿春,這個對象可以表示任何有如下函數(shù)簽名的可調(diào)用對象的,

bool(const std::unique_ptr<Widget>)&,     // C++11里面用來比較兩個
        const std::unique_ptr<Widget>&)   //std::unique_ptr<Widget>對象的函數(shù)簽名

你可以這么寫采盒,

std::function<bool(const std::unique_ptr<Widget>&, 
                     const  std::unique_ptr<Widget>&)> func;

這是因?yàn)閘ambda表達(dá)式產(chǎn)生了可調(diào)用的對象旧乞,這個對象這里稱做一個閉包(closure),可以保存在std::function對象里面磅氨。
closure(閉包)的定義是尺栖,一個函數(shù)和它所引用的非本地變量(非lambda表達(dá)式內(nèi)部定義的變量)的一個集合。

2. 使用std::function作為函數(shù)入?yún)?/h1>

2.1 基于傳值的方式傳遞參數(shù)

參看下面一段代碼烦租,實(shí)現(xiàn)了一個注冊回調(diào)函數(shù)的機(jī)制延赌,

#include <fonctional>
void registerCallBack(std::function<void()>);

入?yún)td::function<void()>是一個模板類對象,它可以用一個函數(shù)簽名為void()的可調(diào)用對象來進(jìn)行初始化叉橱;上述實(shí)現(xiàn)里面是一個傳值調(diào)用挫以。我們來看一下它的調(diào)用過程,

// 方法(A)
registerCallBack([=]{
    ....  // 回調(diào)函數(shù)的實(shí)現(xiàn)部分
})

這里使用了lambda表達(dá)式作為函數(shù)的入?yún)⑶宰#缜懊嫠f的lambda表達(dá)式會生成一個匿名的閉包(closure)掐松,基于這個閉包構(gòu)造了一個std::function<void()>的對象,然后通過傳值調(diào)用的方式把這個對象傳遞registerCallBack函數(shù)中使用粪小。

2.2 基于引用的方式傳遞參數(shù)

當(dāng)然我們還可以如下實(shí)現(xiàn)這個注冊函數(shù)大磺,入?yún)⑼ㄟ^const引用的方式傳遞,這里的引用必須是const的探膊,這是因?yàn)檎{(diào)用registerCallBack函數(shù)的地方生成了一個臨時的std::function()對象量没,是一個右值,否則編譯會報錯突想。

//方法(B)
void registerCallBack(std::function<void()> const&)

這兩者的區(qū)別就在于,在registerCallBack函數(shù)內(nèi)部怎么使用這個入?yún)⒕孔ィ绻皇呛唵蔚恼{(diào)用一下std::func()類猾担,那么兩種都沒有問題,可能使用引用的效率更高刺下;如果register函數(shù)內(nèi)部需要保存這個std::func()绑嘹,并用于以后使用,那么方法A直接保存沒有問題橘茉,方法B就必須做一次拷貝工腋,否則方法B中姨丈,當(dāng)臨時的對象銷毀時,有可能出現(xiàn)引用懸空的問題擅腰。

2.3 傳值方式下的std::function對象保存

如果我們要在registerCallBack函數(shù)內(nèi)部保存這個傳入的function對象蟋恬,我們可以使用轉(zhuǎn)移操作std::move,這樣的效率更高趁冈,

class CallBackHolder 
{
public:
  void registerCallBack(std::function<void()> func)
  {
    callback = std::move(func);
  } 
private:
  std::function<void()> callback; 
}

3. 類的成員函數(shù)作為函數(shù)入?yún)?/h1>

類的成員函數(shù)都會默認(rèn)有個隱藏的this指針歼争,所以不像普通的函數(shù)直接作為入?yún)⒕涂梢粤恕?/p>

3.1 使用std::bind()和std::function來實(shí)現(xiàn)

std::function是通用的多態(tài)函數(shù)封裝器,它的實(shí)例可以存儲渗勘、復(fù)制以及調(diào)用任何可以調(diào)用的目標(biāo):函數(shù)沐绒,lambda表達(dá)式/bind表達(dá)式或其他函數(shù)對象,還有指向成員函數(shù)指針和指向數(shù)據(jù)成員指針旺坠;
std::bind接受一個函數(shù)(或者函數(shù)對象)乔遮,生成一個重新組織的函數(shù)對象;
看下面一個例子取刃,classA提供了一個注冊函數(shù)蹋肮,用來注冊一個回調(diào)函數(shù)

class classA
{
typedef std::function<void(int i)> callback_t;
...
    void registCb(callback_t func)
    {cbHandle = std::move(func);}
private:
    callback_t cbHandle;
};

另一個類classB需要注冊自己的一個成員函數(shù)作為回調(diào)函數(shù)到classA中,這里就可以使用std::bind函數(shù)來實(shí)現(xiàn)蝉衣,

class classB
{
public:
    classB(classA& cA) 
    {       
        cA.registCb(std::bind(&classB::handle, this, std::placeholders::_1));
    }
};
  • bind函數(shù)中顯示的傳遞classB的this指針作為第一個參數(shù)給回調(diào)函數(shù)括尸;
  • std::placeholders:_1代表一個占位符,用于回調(diào)函數(shù)顯式的入?yún)ⅲ?/li>

Effective Modern C++中專門有一節(jié)解釋過std::bind的方式比較繁瑣病毡,并且有時侯會有一些局限性濒翻,所以在引入了lambda表達(dá)式后就可以用lambda表達(dá)式來替代std::bind實(shí)現(xiàn)函數(shù)回調(diào)注冊。

3.2 使用lambda表達(dá)式實(shí)現(xiàn)

使用lambda表達(dá)式的方式可以簡化這一個過程啦膜,參看如下一段代碼有送,classB注冊一個成員函數(shù)作為回調(diào)函數(shù)到classA中,classA會保存這個回調(diào)函數(shù)(std::function對象)到成員變量中僧家,用于后面使用雀摘,

#include <iostream>
#include <functional>
#include <memory>

class classA
{
typedef std::function<void(int i)> callback_t;

public:
    classA() {}
    ~classA() {}
    
    void handle(int i)
    {
        std::cout << "classA::handle" << std::endl;
        
        cbHandle(i);
    }

    void registCb(callback_t func)
    {cbHandle = std::move(func);}
private:
    callback_t cbHandle;
};

class classB
{
public:
    classB(classA& cA) 
    {       
        cA.registCb([this](int i){classB::handle(i);});
    }
    ~classB() {}
    
    void handle(int i)
    {
        std::cout << "classB, handle message" << i << std::endl;
    }
};

int main()
{
    classA testa;
    classB testb(testa);

    testa.handle(10);
}
  • lambda表達(dá)式中捕獲了classB的this指針
  • 使用std::move的方式保存function對象到classA中
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者

  • 序言:七十年代末,一起剝皮案震驚了整個濱河市八拱,隨后出現(xiàn)的幾起案子阵赠,更是在濱河造成了極大的恐慌,老刑警劉巖肌稻,帶你破解...
    沈念sama閱讀 219,188評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件清蚀,死亡現(xiàn)場離奇詭異,居然都是意外死亡爹谭,警方通過查閱死者的電腦和手機(jī)枷邪,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,464評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來诺凡,“玉大人东揣,你說我怎么就攤上這事践惑。” “怎么了嘶卧?”我有些...
    開封第一講書人閱讀 165,562評論 0 356
  • 文/不壞的土叔 我叫張陵尔觉,是天一觀的道長。 經(jīng)常有香客問我脸候,道長穷娱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,893評論 1 295
  • 正文 為了忘掉前任运沦,我火速辦了婚禮泵额,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘携添。我一直安慰自己嫁盲,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,917評論 6 392
  • 文/花漫 我一把揭開白布烈掠。 她就那樣靜靜地躺著羞秤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪左敌。 梳的紋絲不亂的頭發(fā)上瘾蛋,一...
    開封第一講書人閱讀 51,708評論 1 305
  • 那天,我揣著相機(jī)與錄音矫限,去河邊找鬼哺哼。 笑死,一個胖子當(dāng)著我的面吹牛叼风,可吹牛的內(nèi)容都是我干的取董。 我是一名探鬼主播,決...
    沈念sama閱讀 40,430評論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼无宿,長吁一口氣:“原來是場噩夢啊……” “哼茵汰!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起孽鸡,我...
    開封第一講書人閱讀 39,342評論 0 276
  • 序言:老撾萬榮一對情侶失蹤蹂午,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后彬碱,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體画侣,經(jīng)...
    沈念sama閱讀 45,801評論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,976評論 3 337
  • 正文 我和宋清朗相戀三年堡妒,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片溉卓。...
    茶點(diǎn)故事閱讀 40,115評論 1 351
  • 序言:一個原本活蹦亂跳的男人離奇死亡皮迟,死狀恐怖搬泥,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情伏尼,我是刑警寧澤忿檩,帶...
    沈念sama閱讀 35,804評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站爆阶,受9級特大地震影響燥透,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜辨图,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,458評論 3 331
  • 文/蒙蒙 一班套、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧故河,春花似錦吱韭、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,008評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至凑阶,卻和暖如春猿规,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背宙橱。 一陣腳步聲響...
    開封第一講書人閱讀 33,135評論 1 272
  • 我被黑心中介騙來泰國打工姨俩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人养匈。 一個月前我還...
    沈念sama閱讀 48,365評論 3 373
  • 正文 我出身青樓哼勇,卻偏偏與公主長得像,于是被迫代替她去往敵國和親呕乎。 傳聞我的和親對象是個殘疾皇子积担,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,055評論 2 355

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

  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,519評論 1 51
  • C++ lambda表達(dá)式與函數(shù)對象 lambda表達(dá)式是C++11中引入的一項(xiàng)新技術(shù),利用lambda表達(dá)式可以...
    小白將閱讀 85,256評論 15 118
  • 函數(shù)和對象 1猬仁、函數(shù) 1.1 函數(shù)概述 函數(shù)對于任何一門語言來說都是核心的概念帝璧。通過函數(shù)可以封裝任意多條語句,而且...
    道無虛閱讀 4,566評論 0 5
  • 接著上節(jié) condition_varible 湿刽,本節(jié)主要介紹future的內(nèi)容的烁,練習(xí)代碼地址。本文參考http:/...
    jorion閱讀 14,795評論 1 5
  • 一直以為到本命年了诈闺,實(shí)際上卻只有二十三渴庆,足夠證明此刻的生活終究還是煩惱多過快活,畢竟能使人忘卻時間的應(yīng)該都是煩惱,...
    super7777777閱讀 175評論 0 1