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

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

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

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

zzw_111_bit ? 來(lái)源:zzw_111_bit ? 作者:zzw_111_bit ? 2023-02-12 09:20 ? 次閱讀

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

首先看一下回調(diào)函數(shù)的官方解釋:回調(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)。這段解釋比較官方。個(gè)人可以簡(jiǎn)單的理解為:**一個(gè)通過(guò)函數(shù)指針調(diào)用的函數(shù)。如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個(gè)函數(shù),回調(diào)函數(shù)不是由該函數(shù)的實(shí)現(xiàn)方直接調(diào)用,而是在特定的事件或條件發(fā)生時(shí)由另外的一方調(diào)用的,用于對(duì)該事件或條件進(jìn)行響應(yīng)。**如果代碼立即被執(zhí)行就稱為同步回調(diào),如果過(guò)后再執(zhí)行,則稱之為異步回調(diào)。

入門案例

int Callback_1(int a)   ///< 回調(diào)函數(shù)1
{
    printf("Hello, this is Callback_1: a = %d ", a);
    return 0;
}

int Callback_2(int b)  ///< 回調(diào)函數(shù)2
{
    printf("Hello, this is Callback_2: b = %d ", b);
    return 0;
}

int Callback_3(int c)   ///< 回調(diào)函數(shù)3
{
    printf("Hello, this is Callback_3: c = %d ", c);
    return 0;
}

int Handle(int x, int (*Callback)(int)) ///< 注意這里用到的函數(shù)指針定義
{
    Callback(x);
}

int main()
{
    Handle(4, Callback_1);
    Handle(5, Callback_2);
    Handle(6, Callback_3);
    return 0;
}

在這個(gè)入門案例中,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)行回調(diào)函數(shù)的調(diào)用,C++則可以通過(guò)引用、Lambda等多種方式來(lái)進(jìn)行,下面進(jìn)行具體的介紹。

函數(shù)指針

首先函數(shù)指針也是一種指針,只不過(guò)指向的是函數(shù)(C語(yǔ)言中沒(méi)有對(duì)象)。然后通過(guò)這個(gè)指針就可以調(diào)用。

int Func(int x);   /*聲明一個(gè)函數(shù)*/
int (*p) (int x);  /*定義一個(gè)函數(shù)指針*/
p = Func;          /*將Func函數(shù)的首地址賦給指針變量p*/
p = &Func;          /*將Func函數(shù)的首地址賦給指針變量p*/

經(jīng)過(guò)上述后,指針變量 p 就指向函數(shù) Func() 代碼的首地址了。下面看一個(gè)具體的例子。

int Max(int x, int y)  //定義Max函數(shù)
{
    if (x > y){
        return x;
    }else{
      return y;
    }
}
int main()
{
  int(*p)(int, int);  //定義一個(gè)函數(shù)指針
  p = Max;  //把函數(shù)Max賦給指針變量p, 使p指向Max函數(shù)
  int c= (*p)(1,2);//通過(guò)函數(shù)指針調(diào)用Max函數(shù)
  printf("%d",c);  
  return 0;
}

p指向Max函數(shù)之后,然后用p調(diào)用Max函數(shù),返回兩個(gè)數(shù)中的最大值。特別注意的是,因?yàn)楹瘮?shù)名本身就可以表示該函數(shù)地址(指針),因此在獲取函數(shù)指針時(shí),可以直接用函數(shù)名,也可以取函數(shù)的地址。

p = Max可以改成 p = &Max;

c = (*p)(a, b) 可以改成 c = p(a, b)

所以函數(shù)指針的通常寫法是

函數(shù)返回值類型 (* 指針變量名) (函數(shù)參數(shù)列表);

在這里指針變量名也可以叫做函數(shù)名,

但是通常可以用typedef進(jìn)行描述

typedef 函數(shù)返回值類型 (* 指針變量名) (函數(shù)參數(shù)列表);

最后需要注意的是,指向函數(shù)的指針變量沒(méi)有 ++ 和 -- 運(yùn)算。

C++類的靜態(tài)函數(shù)作為回調(diào)函數(shù)

前面函數(shù)指針的方式作為回調(diào)函數(shù)的一種方式,可以同時(shí)用于C和C++,下面介紹另外的一些方式,因?yàn)镃++引入了對(duì)象的概念,可以使用類的成員和靜態(tài)函數(shù)作為回調(diào)函數(shù)。

class ProgramA {
 public:
  void FunA1() { printf("I'am ProgramA.FunA1() and be called..\\n"); }

  static void FunA2() { printf("I'am ProgramA.FunA2() and be called..\\n"); }
};
class ProgramB {
 public:
  void FunB1(void (*callback)()) {
    printf("I'am ProgramB.FunB1() and be called..\\n");
    callback();
  }
};
int main(int argc, char **argv) {
  ProgramA PA;
  PA.FunA1();

  ProgramB PB;
  PB.FunB1(ProgramA::FunA2);
}

在類B中調(diào)用類A中的靜態(tài)函數(shù)作為回調(diào)函數(shù),從而實(shí)現(xiàn)了回調(diào)。但這種實(shí)現(xiàn)有一個(gè)很明顯的缺點(diǎn):static 函數(shù)不能訪問(wèn)非static 成員變量或函數(shù),會(huì)嚴(yán)重限制回調(diào)函數(shù)可以實(shí)現(xiàn)的功能。

類的非靜態(tài)函數(shù)作為回調(diào)函數(shù)

這種方式比較麻煩,可以先看一下下面的例子。

class ProgramA {
 public:
  void FunA1() { printf("I'am ProgramA.FunA1() and be called..\\n"); }

  void FunA2() { printf("I'am ProgramA.FunA2() and be called..\\n"); }
};

class ProgramB {
 public:
  void FunB1(void (ProgramA::*callback)(), void *context) {
    printf("I'am ProgramB.FunB1() and be called..\\n");
    ((ProgramA *)context->*callback)();
  }
};
int main(int argc, char **argv) {
  ProgramA PA;
  PA.FunA1();

  ProgramB PB;
  PB.FunB1(&ProgramA::FunA2, &PA);  // 此處都要加&
}

功能總體與上面一個(gè)相同,但是,類的靜態(tài)函數(shù)本身不屬于該類,所以和普通函數(shù)作為回調(diào)函數(shù)類似。這種方式存在一些不足,,也就我預(yù)先還要知道回調(diào)函數(shù)所屬的類定義,當(dāng)ProgramB想獨(dú)立封裝時(shí)就不好用了。(違背了一些設(shè)計(jì)模式的原則)

Lambda表達(dá)式作為回調(diào)函數(shù)

Lambda本身就是一種匿名函數(shù),是一種函數(shù)的簡(jiǎn)寫形式(此處參考上一篇博客Lambda表達(dá)式)

#include 
#include
void func1(int a,std::function<void(int)> func2){
  func2(a);
}
int main(int argc, char **argv) {
  auto fun3 = [](int a){
    std::cout<std::endl;
  };
  func1(3,fun3);
}

這種方式也較為簡(jiǎn)單,但要注意在C++11版本才開(kāi)始引入Lambda表達(dá)式,在一些較為老舊的編譯器上可能無(wú)法通過(guò)。

std::funtion和std::bind的使用

這種方式也是適用于C++,要引入functional的頭文件。存儲(chǔ)、復(fù)制、和調(diào)用操作,這些目標(biāo)實(shí)體包括普通函數(shù)、Lambda表達(dá)式、函數(shù)指針、以及其它函數(shù)對(duì)象等。std::bind()函數(shù)的意義就像它的函數(shù)名一樣,是用來(lái)綁定函數(shù)調(diào)用的某些參數(shù)的。

#include 

#include  // fucntion/bind

class ProgramA {
 public:
  void FunA1() { printf("I'am ProgramA.FunA1() and be called..\\n"); }

  void FunA2() { printf("I'am ProgramA.FunA2() and be called..\\n"); }

  static void FunA3() { printf("I'am ProgramA.FunA3() and be called..\\n"); }
};

class ProgramB {
  typedef std::function<void ()> CallbackFun;
 public:
   void FunB1(CallbackFun callback) {
    printf("I'am ProgramB.FunB2() and be called..\\n");
    callback();
  }
};

void normFun() { printf("I'am normFun() and be called..\\n"); }

int main(int argc, char **argv) {
  ProgramA PA;
  PA.FunA1();

  printf("\\n");
  ProgramB PB;
  PB.FunB1(normFun);
  printf("\\n");
  PB.FunB1(ProgramA::FunA3);
  printf("\\n");
  PB.FunB1(std::bind(&ProgramA::FunA2, &PA));
}

主要看最后一行,通過(guò)std::bind函數(shù)綁定了對(duì)象與對(duì)應(yīng)的函數(shù),這種方式比上面的通過(guò)類的成員函數(shù)進(jìn)行回調(diào)更為簡(jiǎn)單方便。下面看一下如果有參數(shù)的話,需要引入占位符std::placeholders::_1來(lái)進(jìn)行回調(diào)。

#include 
#include 
using namespace std;
 
int TestFunc(int a, char c, float f)
{
    cout << a << endl;
    cout << c << endl;
    cout << f << endl;
 
    return a;
}
 
int main()
{
    auto bindFunc1 = bind(TestFunc, std::placeholders::_1, 'A', 100.1);
    bindFunc1(10);
 
    cout << "=================================\\n";
 
    auto bindFunc2 = bind(TestFunc, std::placeholders::_2, std::placeholders::_1, 100.1);
    bindFunc2('B', 10);
 
    cout << "=================================\\n";
 
    auto bindFunc3 = bind(TestFunc, std::placeholders::_2, std::placeholders::_3, std::placeholders::_1);
    bindFunc3(100.1, 30, 'C');
 
    return 0;
}

上述例子中引入了占位符std::placeholders::_1,可以有多個(gè),通過(guò)下劃線加數(shù)字來(lái)實(shí)現(xiàn),從而實(shí)現(xiàn)有參數(shù)的回調(diào)。這個(gè)bind函數(shù)中的重載通常第一個(gè)是函數(shù)的指針,第二個(gè)是調(diào)用對(duì)象的指針,后面跟上參數(shù)占位符。
審核編輯:湯梓紅

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(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)投訴
  • 函數(shù)
    +關(guān)注

    關(guān)注

    3

    文章

    4260

    瀏覽量

    62228
  • 指針
    +關(guān)注

    關(guān)注

    1

    文章

    475

    瀏覽量

    70477
  • C++
    C++
    +關(guān)注

    關(guān)注

    21

    文章

    2090

    瀏覽量

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

    關(guān)注

    0

    文章

    87

    瀏覽量

    11521
收藏 人收藏

    評(píng)論

    相關(guān)推薦

    詳解C語(yǔ)言函數(shù)指針與調(diào)函數(shù)

    在講調(diào)函數(shù)之前,我們需要了解函數(shù)指針。
    發(fā)表于 10-19 09:34 ?753次閱讀

    C語(yǔ)言里面的函數(shù)指針和調(diào)函數(shù)

    在講調(diào)函數(shù)之前,我們需要了解函數(shù)指針。
    發(fā)表于 12-13 10:28 ?498次閱讀

    C 語(yǔ)言調(diào)函數(shù)詳解

    C 語(yǔ)言調(diào)函數(shù)詳解什么是調(diào)
    發(fā)表于 04-08 10:36

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

    C/C++調(diào)函數(shù)首先看一下回調(diào)
    發(fā)表于 02-11 15:25

    C語(yǔ)言調(diào)函數(shù)學(xué)習(xí)

    對(duì)指針的應(yīng)用是C語(yǔ)言編程的精髓所在,而回調(diào)函數(shù)就是C語(yǔ)言里面對(duì)函數(shù)指針的高級(jí)應(yīng)用。簡(jiǎn)而言之,
    發(fā)表于 05-27 09:44 ?7178次閱讀

    C語(yǔ)言函數(shù)調(diào)函數(shù)

    來(lái)源:嵌入式客棧 1 什么是調(diào)函數(shù)?首先什么是調(diào)呢? 我的理解是:把段可執(zhí)行的代碼像參數(shù)傳
    的頭像 發(fā)表于 09-11 09:57 ?4068次閱讀

    CC++一回事嗎

    C89,C++標(biāo)準(zhǔn)是C++99。 我們來(lái)介紹C語(yǔ)言和C++中那些不同的地方。 函數(shù)默認(rèn)值 在
    的頭像 發(fā)表于 11-13 18:18 ?3222次閱讀

    c語(yǔ)言調(diào)函數(shù)的使用及實(shí)際作用詳解

    大家好,我是無(wú)際。今天給大家講下芯片/模塊廠家寫SDK必須會(huì)使用的種技術(shù):調(diào)函數(shù)
    發(fā)表于 11-20 19:51 ?13次下載
    <b class='flag-5'>c</b>語(yǔ)言<b class='flag-5'>回</b><b class='flag-5'>調(diào)</b><b class='flag-5'>函數(shù)</b>的使用及實(shí)際作用<b class='flag-5'>詳解</b>

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

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

    詳解事件調(diào)VI

    通過(guò)事件調(diào)注冊(cè)函數(shù)(Register Event Callback)注冊(cè)個(gè)調(diào)VI,在事件發(fā)
    的頭像 發(fā)表于 11-24 09:13 ?1707次閱讀

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

    C++中的個(gè)重要概念就是類,所以我們般想讓類的成員函數(shù)作為調(diào)
    的頭像 發(fā)表于 01-18 15:09 ?3357次閱讀

    C語(yǔ)言技巧之調(diào)函數(shù)

    在講調(diào)函數(shù)之前,我們需要了解函數(shù)指針。
    的頭像 發(fā)表于 04-18 11:50 ?698次閱讀

    C語(yǔ)言|調(diào)函數(shù)的不同用法

    調(diào)函數(shù)是個(gè)高級(jí)操作技巧,也是日常項(xiàng)目中常常使用到的技能。之所以說(shuō)調(diào)函數(shù)是個(gè)高級(jí)操作技巧,是因
    發(fā)表于 07-10 10:34 ?1197次閱讀

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

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

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

    調(diào)函數(shù)種特殊的函數(shù),它作為參數(shù)傳遞給另個(gè)函數(shù)
    發(fā)表于 03-12 11:46 ?2469次閱讀