C++学习笔记(四)回调函数的基本使用和总结

什么是回调函数

在写这篇博文之前,我对于回调函数的概念都是十分模糊的。这篇博文我将通过一些实际例子的分析来说明一下回调函数是什么

简单粗暴的总结

回调函数,就是一个函数的参数是另外一个函数

下面先用C语言写个最简单的例子
#include <stdio.h>

/*回调函数1*/
int callBack1(int x)
{
    
    
	return x*2;
}
/*回调函数2*/
int callBack2(int x)
{
    
    
	return x/2;
}
/*中间函数*/
int calc(int b,int (* func)(int))   //func 把函数当作参数来进行传递
{
    
    
	return 100+func(b);
}
int main()
{
    
    
	int a=100;
	
	/*调用回调函数1*/
	int q=calc(20,callBack1);
	printf("%d\n",q);

	/*调用回调函数2*/
	int p=calc(40,callBack2);
	printf("%d\n",p);

	return 0;
}

运行结果
在这里插入图片描述

简单概括一下上面的程序:

在这里我定义了两个回调函数,分别是回调函数1,回调函数2。在main函数里面通过传递参数来将回调函数传参到一个中间函数里(注意:这里要用函数指针的方式来接收)。通过传入的回调函数不一样,从而拥有不一样的结果。
这里就可以简单地用我理解的方式来概括一下回调函数是什么:回调函数就是把它本身以函数指针的形式传参到一个中间函数里面,当我们调用这个中间函数的同时,将会执行这个回调函数。也就是说,我们什么时候调用这个中间函数,就会执行传参进去的回调函数。
例如我们常用的创建线程函数(pthread_create),就是我们上面的中间函数。我们传参进去的函数指针,也就是传说中的的回调函数。当我们在执行pthread_create这个函数时,在pthread_create函数内部会执行传参进去的函数,具体怎么执行的,这里不过多讨论。

回调函数的经典用法

回调函数当然不仅仅是用来装b的,它自然有它独到的用法。下面介绍一下我在公司学到的回调函数的用法。

场景模拟

在我们做嵌入式开发时,数据(例如摄像头获取的图像,雷达获取的坐标)一般是由底层传输到上层,如果我们在同一个.cpp文件里面去同时做数据采集和数据处理,那么代码未免也太过复杂了,也不方便维护。所以我们必须将底层的硬件配置与上层的数据处理相分离开。知道了我们的目的,下面我写了一个简化版的程序框架。

main.cpp
#include <iostream>
#include "msgCommon.h"

using namespace std;

/* 底层上来的数据在这里获取 */
void msgCallBack(int pUserData)
{
    
    
    cout<<"from msgCommon.cpp get message:"<<pUserData<<endl;
}

int main()
{
    
    
    int nRet; 
    /* 创建实例化对象 */
    callBackTest test;

    /* 设置msgCallBack为回调函数 */
    test.SetMsgCallBack(msgCallBack, 0);

    /* 模拟从底层获取数据 */
    nRet = test.getData();
    if(false == nRet)
    {
    
    
        cout<<"get Data failure"<<endl;
    }

    return 0;
}

msgCommon.cpp
#include <iostream>
#include "msgCommon.h"

using namespace std;

/* 将main.cpp里面传进来的数据与类里面的一一对应 */
bool callBackTest::SetMsgCallBack(pMsgCallBack pCallBack, int pUserData)
{
    
    
    m_pCallBack = pCallBack;
    m_pUserData = pUserData;
    return true;
}

/* 用来将数据传递给上层 */
bool callBackTest::notify(int c)
{
    
    
    m_pUserData = c;
    if(m_pCallBack)
    {
    
    
        m_pCallBack(m_pUserData);
        return true;
    }

    return false;
}

/* 用于接收数据,这里简单用键盘输入一个整型数 */
bool callBackTest::getData()
{
    
    
    bool nRet; 
    int c;
    /* 输入数据 */
    cout<<"please input your data:";
    cin>>c;
    cout<<"input success,your data is:"<<c<<endl;

    /* 传递数据给上层 */
    nRet = notify(c);
    if(false == nRet)
    {
    
    
        cout<<"failure"<<endl;
    }

    return true;

}
msgCommon.h
#pragma once //避免重复包含头文件

/* 声明回调函数的类型 */
typedef void (*pMsgCallBack)(int pUserData);

class callBackTest
{
    
    
  public:
    /* 设置回调函数的接口 */
    bool            SetMsgCallBack(pMsgCallBack pCallBack, int pUserData);

    /* 用于将数据传递给上层 */
    bool            notify(int c);

    bool            getData(void);
  
  private:
    /* 回调函数 */
    pMsgCallBack	m_pCallBack;
   
    /* 用户数据 */
    int            m_pUserData; 
};
Makefile
test: main.o msgCommon.o
	g++ -o $@ $^

%.o : %.cpp
	g++ -c -o $@ $<

clean:
	rm -f *.o test

运行结果
在这里插入图片描述

简单概括一下以上的程序:

这里在main.cpp(这里可以理解成我们的上层应用)里面定义了一个回调函数(msgCallBack),等待获取来自底层的数据,底层获取到数据后,就会执行main.cpp里面的回调函数,我们可以在这个函数里对来自底层的数据进行处理,想怎么玩怎么玩,怎么改都不会影响底层的程序。这就是我们的目的。

下面简单梳理一下这个框架的逻辑:

我们main函数里面的回调函数叫msgCallBack
在这里插入图片描述
通过传参传递到msgCommon.cpp里面
在这里插入图片描述

在这里插入图片描述
这里在callBackTest类里面声明了一个相同类型的回调函数(m_pCallBack)去将main函数里面的回调函数接住,因为传送的是函数指针,是地址,所以之后,我们操作(m_pCallBack)这个函数也就相当于操作main函数里面的(msgCallBack)
之后我们执行了callBackTest类里面的getData()函数
在这里插入图片描述
用键盘输入一个整型数来模拟我们从底层获取到数据,获取到了数据后,调用notify这个函数
在这里插入图片描述
将我们获取到的数据传输到m_pCallBack这个函数里面,也就是调用了msgCallBack(因为两者是同一个东西),因为调用了msgCallBack,数据自然就来到了我们的应用程序。
在这里插入图片描述

这里就出现了我们刚刚所说的模型。notify这个函数就相当于我们刚刚C语言程序中的中间函数,m_pCallBack/msgCallBack就相当于回调函数。

总结

回调函数可以很好地实现上层和底层之间的数据交互,实现了将大型工程代码分层的思想。这里只是举了个简单例子,夹带了很多个人的主观看法。因为本人能力有限,如有纰漏,希望大家不吝指正。

猜你喜欢

转载自blog.csdn.net/Stone831143/article/details/112117639