0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線(xiàn)課程
  • 觀看技術(shù)視頻
  • 寫(xiě)文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

介紹在C++中實(shí)現(xiàn)回調(diào)的幾種方法

冬至子 ? 來(lái)源:技術(shù)鋪?zhàn)?/span> ? 作者: chasenzhang ? 2023-01-18 15:09 ? 次閱讀

從C的回調(diào)函數(shù)說(shuō)起

C語(yǔ)言中,回調(diào)函數(shù)是一個(gè)非常重要的概念,它的定義

回調(diào)函數(shù)就是一個(gè)通過(guò)函數(shù)指針調(diào)用的函數(shù)。如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個(gè)函數(shù),當(dāng)這個(gè)指針被用來(lái)調(diào)用其所指向的函數(shù)時(shí),我們就說(shuō)這是回調(diào)函數(shù)。

回調(diào)函數(shù)不是由該函數(shù)的實(shí)現(xiàn)方直接調(diào)用,而是在特定的事件或條件發(fā)生時(shí)由另外的一方調(diào)用的,用于對(duì)該事件或條件進(jìn)行響應(yīng)。

其實(shí)說(shuō)白了就是把一個(gè)函數(shù)當(dāng)做參數(shù)傳下去。

C中實(shí)現(xiàn)回調(diào)函數(shù)比較簡(jiǎn)單,請(qǐng)看下面這個(gè)例子:

#include 


typedef void (*p_CB)(int);
void CB(int a)              { printf("CB a = %d\\n", a); }
void runCB(p_CB CB, int a)  { printf("run CB\\n"); CB(a); }


int main()
{
    p_CB cb = &CB;
    runCB(cb, 20);
    return 0;
}

C++中的回調(diào)函數(shù)的問(wèn)題

在C++中的一個(gè)重要概念就是類(lèi),所以我們一般想讓類(lèi)的成員函數(shù)作為回調(diào)函數(shù)(如果直接用非類(lèi)的成員函數(shù)作為回調(diào)函數(shù),其實(shí)就和C語(yǔ)言中的方法一樣),但是想實(shí)現(xiàn)這樣的功能,還是存在一些限制的。

主要原因是,類(lèi)的成員函數(shù)的參數(shù)都隱藏了this指針這個(gè)參數(shù),既然多加了一個(gè)參數(shù),那么成員函數(shù)的參數(shù)個(gè)數(shù)就和定義的回調(diào)函數(shù)個(gè)數(shù)不匹配,導(dǎo)致調(diào)用失敗。

舉個(gè)例子:

#include 


typedef void (*p_CB)(int);
void runCB(p_CB CB, int a) { printf("run CB\\n"); CB(a); }


class B
{
public:
    void CB(int a) { printf("CB a = %d\\n", a); }
};


int main()
{
    B b;
    runCB(B::CB, 20); //錯(cuò)誤
    return 0;
}

我們這樣寫(xiě),在15行會(huì)報(bào)下面的錯(cuò)誤。

test_class_cb.cpp: In functionint main()’:
test_class_cb.cpp:15:14: error: invalid use of non-static member functionvoid B::CB(int)’
     runCB(B::CB, 20); //錯(cuò)誤

報(bào)的錯(cuò)說(shuō)使用了非靜態(tài)的函數(shù),分析下原因,其實(shí)是B::CB的函數(shù)是非靜態(tài)成員函數(shù),它隱藏了this參數(shù),導(dǎo)致p_CB和B::CB這兩個(gè)函數(shù)的參數(shù)不匹配。

而編譯器報(bào)的這個(gè)錯(cuò)誤,其實(shí)也是同樣的意思,編譯器說(shuō)我們調(diào)用的不是非靜態(tài)函數(shù),當(dāng)我們把B::CB定義成靜態(tài)函數(shù)也就不會(huì)報(bào)錯(cuò)了(也就是去掉了this指針)。

C++中的回調(diào)函數(shù)的實(shí)現(xiàn)

靜態(tài)函數(shù)

既然成員函數(shù)都會(huì)存在this指針,那么我們可以去掉this指針呢?其實(shí)是可以的,我們把成員函數(shù)定義為靜態(tài)函數(shù),靜態(tài)函數(shù)是屬于整個(gè)類(lèi)的,不屬于某個(gè)對(duì)象,也就沒(méi)有this指針了。

這個(gè)例子的話(huà),就是我們?cè)谏弦粋€(gè)章節(jié)提到的,大家可以直接加上static。

這種方法其實(shí)有一定的缺點(diǎn),靜態(tài)會(huì)帶來(lái)諸多的不變,因?yàn)殪o態(tài)函數(shù)沒(méi)有this指針,那么它就訪(fǎng)問(wèn)不了普通的成員函數(shù)或者數(shù)據(jù)成員,會(huì)給我們?cè)陂_(kāi)發(fā)中帶來(lái)不小的影響,特別是工程比較大的時(shí)候。

成員函數(shù)的函數(shù)指針

這個(gè)方法從另外一個(gè)角度出發(fā),我們可以定義一個(gè)成員函數(shù)的函數(shù)指針,其實(shí)相當(dāng)于這個(gè)函數(shù)指針有了this參數(shù)了。

例子如下:

#include 


class B
{
public:
    void CB(int a) { printf("CB a = %d\\n", a); }
};


typedef void (B::*p_CB)(int);
// using p_CB = void (B::*)(int);
void runCB(p_CB CB, B b, int a)  { printf("run CB\\n"); (b.*CB)(a); }


int main()
{
    B b;
    p_CB p_f1 = &B::CB;
    runCB(p_f1, b, 20);
    return 0;
}

這種寫(xiě)法晦澀難懂,特別是(b.*CB)(a)這種寫(xiě)法一看上去就覺(jué)得很奇怪,代碼寫(xiě)的越生僻,越復(fù)雜,帶來(lái)的風(fēng)險(xiǎn)就越大。

另外,如果想要采用這種方法實(shí)現(xiàn)回調(diào),那么在定義回調(diào)函數(shù)的時(shí)候就得知道誰(shuí)會(huì)調(diào)用它,顯然,這個(gè)要求是不合理的,所以,這種方法很不常用,只是介紹這種方法。

單例模式

這個(gè)方法是上面靜態(tài)函數(shù)方法的進(jìn)一步優(yōu)化,因?yàn)殪o態(tài)函數(shù)沒(méi)有傳進(jìn)去this指針,那么我們可不可以想辦法得到this指針呢?單例模式就可以做到,一般上單例模式為了讓類(lèi)只存在一個(gè)對(duì)象,一般都會(huì)在類(lèi)的內(nèi)部定義一個(gè)指向自己的指針,我們就可以通過(guò)這個(gè)指針,在任何地方都可以訪(fǎng)問(wèn)到這個(gè)對(duì)象。

簡(jiǎn)單例子:

#include 


class B
{
public:
    static B *get_instance() {
        static B instance;
        return &instance;
    }
    void CB(int a) { printf("CB a = %d\\n", a); }
};


void runCB(int a)
{
    printf("run CB\\n"); 
    B *b_ptr = B::get_instance();
    b_ptr->CB(a);
}


int main(int argc, char *argv[])
{
  B *b_ptr = B::get_instance();
    runCB(20);
    return 0;
}

使用這種方法,在任何地方都可以訪(fǎng)問(wèn)該對(duì)象,可以很方便得調(diào)用對(duì)象的方法,是比較好的一種方法了,需要注意的就是單例模式的一些限制而已。

另外說(shuō)一下,單例模式只能說(shuō)是一種可以解決回調(diào)函數(shù)的辦法,不一定是因?yàn)橐卣{(diào),才設(shè)計(jì)成單例。

C++11的function和bind

如果你的編譯器支持C++11標(biāo)準(zhǔn)的話(huà)(現(xiàn)在編譯器基本上都會(huì)支持),還有一種更加方便快捷的方法。

function模板

首先介紹一下什么叫 可調(diào)用對(duì)象 ,簡(jiǎn)單來(lái)說(shuō)就是可以使用()來(lái)調(diào)用的對(duì)象,在C++11中,包括函數(shù)、函數(shù)指針、lambda表達(dá)式、bind創(chuàng)建的對(duì)象和重載了()的類(lèi)。

function是一個(gè)模板函數(shù),它的功能和函數(shù)指針很像,但是函數(shù)指針只能針對(duì)非成員函數(shù),而function則可以調(diào)用C++中所有的可調(diào)用實(shí)體。

對(duì)于成員函數(shù)來(lái)說(shuō),function不能直接調(diào)用,這就需要通過(guò)下面的bind來(lái)做“媒介”。

bind函數(shù)

bind函數(shù)簡(jiǎn)單介紹,就是可以綁定到任意一個(gè)可調(diào)用對(duì)象,下面是它的定義。

可以講bind看作一個(gè)通用的函數(shù)適配器,他接受一個(gè)可調(diào)用對(duì)象,生成一個(gè)新的可調(diào)用對(duì)象來(lái)“適應(yīng)”原對(duì)象的參數(shù)列表。 ----《c++Primer》

也就是說(shuō),通過(guò)bind綁定類(lèi)的非靜態(tài)成員函數(shù),生成一個(gè)可調(diào)用對(duì)象,然后通過(guò)function模板去調(diào)用這個(gè)可調(diào)用對(duì)象,也就實(shí)現(xiàn)了調(diào)用類(lèi)的非靜態(tài)成員函數(shù)。

下面通過(guò)一個(gè)簡(jiǎn)單的例子來(lái)說(shuō)明如何使用。

例子

#include 
#include 
// typedef std::function
using funCB = std::function<void(int)>; //可以這樣寫(xiě),更直觀
class B
{
public:
    void setCB(funCB CB)    { m_CB = CB; }
    void runCB(int a)       { std::cout << "runCB" << std::endl; m_CB(a); }
    void printB(int a)      { std::cout << "a = " << a << std::endl; }
    funCB m_CB;
};
class C
{
public:
    void cbfun(int a) { std::cout << "CB a = "
                        << a << std::endl; }
};
int main()
{
    B b; C c;
    funCB fun = std::bind(&C::cbfun, &c, std::placeholders::_1);
    b.setCB(fun);
    b.runCB(20);
    return 0;
}

在這個(gè)例子中,b.runCB(),然后會(huì)調(diào)用m_CB函數(shù),而m_CB通過(guò)setCB綁定到了C::cbfun,所以C::cbfun也會(huì)被調(diào)用。也就是就是通過(guò)b的成函數(shù)調(diào)用到了c的成員函數(shù),實(shí)現(xiàn)了回調(diào)。

這種方式是不是看起很簡(jiǎn)單呢,

后記

這邊文章介紹了在C++中實(shí)現(xiàn)回調(diào)的幾種方法,總的來(lái)說(shuō)C++11的function&bind是比較好的方法,另外說(shuō)一點(diǎn),function的bind的功能不止這些,這里介紹的只是他們的一種用法。

審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫(xiě)或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場(chǎng)。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問(wèn)題,請(qǐng)聯(lián)系本站處理。 舉報(bào)投訴
  • C語(yǔ)言
    +關(guān)注

    關(guān)注

    180

    文章

    7595

    瀏覽量

    135916
  • 編譯器
    +關(guān)注

    關(guān)注

    1

    文章

    1617

    瀏覽量

    49019
  • 回調(diào)函數(shù)
    +關(guān)注

    關(guān)注

    0

    文章

    87

    瀏覽量

    11530
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    Android NDK編程--- C/C++調(diào)用Java不同類(lèi)的靜態(tài)方法

    前言上一篇我們介紹了《Android NDK編程(四)--- C/C++調(diào)用Java方法》,主要是C
    發(fā)表于 07-02 07:56

    調(diào)函數(shù)程序開(kāi)發(fā)中有何作用呢

    調(diào)函數(shù)程序開(kāi)發(fā)是一個(gè)非常重要的概念,所謂的調(diào)其實(shí)就是不同程序模塊之間的接口和約定,是軟件
    發(fā)表于 03-01 07:13

    如何在screenViewBase寫(xiě)這兩個(gè)調(diào)?

    你好我想使用 clicklistener 和按鈕調(diào)。如何在screenViewBase寫(xiě)這兩個(gè)調(diào)?screenViewBase::sc
    發(fā)表于 01-30 07:36

    C/C++調(diào)函數(shù)

    ;#125;在這個(gè)入門(mén)案例,Callback_1、2、3就是調(diào)函數(shù),handle函數(shù)的第二個(gè)參數(shù)就是函數(shù)指針,也就是通過(guò)函數(shù)指針來(lái)調(diào)用。純C語(yǔ)言通過(guò)函數(shù)指針來(lái)進(jìn)行
    發(fā)表于 02-11 15:25

    (純干貨)使用STM32測(cè)量頻率和占空比的幾種方法

    本文詳細(xì)介紹了使用STM32測(cè)量頻率和占空比的幾種方法。
    的頭像 發(fā)表于 03-13 15:43 ?4.3w次閱讀
    (純干貨)使用STM32測(cè)量頻率和占空比的<b class='flag-5'>幾種方法</b>

    VISUAL C++教程之VISUAL C++的安裝和使用方法

    本文檔的主要內(nèi)容詳細(xì)介紹的是VISUAL C++教程之VISUAL C++的安裝和使用方法資料免費(fèi)下載。
    發(fā)表于 12-27 16:32 ?19次下載
    VISUAL <b class='flag-5'>C++</b>教程之VISUAL <b class='flag-5'>C++</b>的安裝和使用<b class='flag-5'>方法</b>

    干貨:計(jì)算fibnacci 級(jí)數(shù)的幾種方法

    干貨:計(jì)算fibnacci 級(jí)數(shù)的幾種方法
    的頭像 發(fā)表于 06-22 11:23 ?2383次閱讀
    干貨:計(jì)算fibnacci 級(jí)數(shù)的<b class='flag-5'>幾種方法</b>

    詳解調(diào)函數(shù)的概念及使用步驟

    調(diào)函數(shù)就是一個(gè)被作為參數(shù)傳遞的函數(shù)。C語(yǔ)言中,調(diào)函數(shù)只能使用函數(shù)指針
    的頭像 發(fā)表于 05-26 15:20 ?3961次閱讀

    一文詳解C/C++調(diào)函數(shù)

    參數(shù)傳遞給另一個(gè)函數(shù),調(diào)函數(shù)不是由該函數(shù)的實(shí)現(xiàn)方直接調(diào)用,而是特定的事件或條件發(fā)生時(shí)由另外的一方調(diào)用的,用于對(duì)該事件或條件進(jìn)行響應(yīng)。**如果代碼立即被執(zhí)行就稱(chēng)為同步
    的頭像 發(fā)表于 02-12 09:20 ?1471次閱讀

    labview事件調(diào)的使用

    介紹LabVIEW事件調(diào)的使用方法,可以用于處理執(zhí)行時(shí)間較長(zhǎng)的異步事件
    的頭像 發(fā)表于 08-24 16:38 ?4046次閱讀
    labview<b class='flag-5'>中</b>事件<b class='flag-5'>回</b><b class='flag-5'>調(diào)</b>的使用

    C++生成Dll與調(diào)函數(shù)測(cè)試

    描述了VS環(huán)境下,通過(guò)C++生成dll的方法,測(cè)試調(diào)函數(shù)
    的頭像 發(fā)表于 08-29 16:05 ?1492次閱讀
    <b class='flag-5'>C++</b>生成Dll與<b class='flag-5'>回</b><b class='flag-5'>調(diào)</b>函數(shù)測(cè)試

    javajvm調(diào)優(yōu)有幾種方法

    JVM調(diào)優(yōu)是Java應(yīng)用程序性能優(yōu)化過(guò)程的重要步驟,它通過(guò)針對(duì)JVM進(jìn)行優(yōu)化來(lái)提高應(yīng)用程序的性能和可靠性。JVM調(diào)優(yōu)可以根據(jù)具體的場(chǎng)景和需求,采用不同的方法和策略進(jìn)行。 首先,我們需
    的頭像 發(fā)表于 12-05 11:11 ?2025次閱讀

    ??嵌入式調(diào)函數(shù)的實(shí)現(xiàn)方法

    調(diào)函數(shù)的命名規(guī)范沒(méi)有固定的標(biāo)準(zhǔn),但是根據(jù)通用慣例和編碼規(guī)范,調(diào)函數(shù)的命名應(yīng)該能夠反映函數(shù)的作用和功能,讓其他開(kāi)發(fā)者能夠快速理解并使用。
    發(fā)表于 03-04 14:49 ?642次閱讀

    調(diào)函數(shù)(callback)是什么?調(diào)函數(shù)的實(shí)現(xiàn)方法

    調(diào)函數(shù)是一種特殊的函數(shù),它作為參數(shù)傳遞給另一個(gè)函數(shù),并在被調(diào)用函數(shù)執(zhí)行完畢后被調(diào)用。調(diào)函數(shù)通常用于事件處理、異步編程和處理各種操作系統(tǒng)和框架的API。
    發(fā)表于 03-12 11:46 ?2661次閱讀

    C++實(shí)現(xiàn)類(lèi)似instanceof的方法

    函數(shù),可實(shí)際上C++沒(méi)有。但是別著急,其實(shí)C++中有兩種簡(jiǎn)單的方法可以實(shí)現(xiàn)類(lèi)似Java的in
    的頭像 發(fā)表于 07-18 10:16 ?510次閱讀
    <b class='flag-5'>C++</b><b class='flag-5'>中</b><b class='flag-5'>實(shí)現(xiàn)</b>類(lèi)似instanceof的<b class='flag-5'>方法</b>