通常认为通过函数指针在其他地方调用函数的过程称为回调,被调用的函数称为回调函数,函数指针通过传参方式传递。
在C++的实际应用中,有很多实现回调函数的方式,不一定要传递函数指针才能实现回调,只要实现了回调的功能,都叫回调函数。
回调函数是和调用者在同一线程,使用中要注意线程安全。
本章通过几个例子介绍C++实现回调函数的几种方式。
C++虚函数特性实现回调函数
这是最具C++特征的回调函数,基类定义纯虚函数,派生类实现虚函数,基类指针可以指向派生类,使用基类指针就可以调用派生类的虚函数,从而实现回调。
不足:利用虚函数特性实现回调函数的方式需要定义基类,通常是子类对父类的回调,当子类层级太多时需要逐级回调,实现起来比较繁琐。
代码实现:
#ifndef MANAGERINH_H
#define MANAGERINH_H
#include<string>
using namespace std;
class messageBase {
//消息回调基类
public:
messageBase() {
};
virtual ~messageBase() {
};
virtual void msgCallback(std::string msg)=0;
};
class employeeINH{
//员工类
public:
employeeINH(messageBase *msgBase){
m_messageBase = msgBase;
}
void dowork(int count) {
int sum = 0;
for(int index=0;index<count;index++)
sum += index*10;
std::string msg = "total:" + to_string(sum);
if(m_messageBase)
m_messageBase->msgCallback(msg);//回调函数
}
private:
messageBase *m_messageBase;//消息回调基类对象指针,可以指向它的派生类,也就是 ManagerINH
};
class ManagerINH : public messageBase{
//经理类,继承于消息回调基类
public:
ManagerINH(){
//创建子对象时,把父对象的指针传递过去
pemployeeINH = new employeeINH(this);
}
virtual void msgCallback(std::string msg){
//实现虚函数
printf("result=%s\n",msg.c_str());
}
void sendwork(int num){
pemployeeINH->dowork(num);
}
private:
employeeINH *pemployeeINH;
};
#endif // MANAGERINH_H
调用测试
ManagerINH mManagerINH;
mManagerINH.sendwork(10);
打印
result=total:450
C++结构体指针实现回调函数
把全局回调函数的指针、定义结构体对象的指针封装在一个结构体里,把这个结构体指针传递给子类,子类的子类等等,可以在程序的任何地方实现回调。
代码实现,managercon.h
#ifndef MANAGERCON_H
#define MANAGERCON_H
#include <string>
//回调结构体
struct ChwContext {
//data 回调数据指针
//nb_data 回调数据长度
//user_this 指向当前结构体的指针
void (*receiveQt)(char *data, int nb_data,void* user_this);
void *user_callback;//回调函数所在对象的指针
};
#define safe_delete(a) {
if( (a)) {
delete (a); (a) = NULL;}}
#define safe_deleteA(a) {
if( (a)) {
delete[] (a); (a) = NULL;}}
#define safe_free(a) {
if( (a)) {
free((a)); (a) = NULL;}}
class EmployeeCON//员工类
{
public:
EmployeeCON(ChwContext *pcontext){
pChwContext = pcontext;
}
ChwContext *pChwContext;//由父对象传递过来的回调结构体指针
void dowork(int count){
int sum = 0;
for(int index=0;index<count;index++)
sum += index*10;
std::string tmpstr = "total:" + std::to_string(sum);
pChwContext->receiveQt((char*)tmpstr.c_str(),tmpstr.size(),pChwContext);//执行回调
}
};
class ManagerCON//经理类
{
public:
ManagerCON();
~ManagerCON();
void sendwork(int count);
void callback_receive(std::string msg);//回调函数
private:
ChwContext *p_ChwContext;//回调结构体,在回调函数所在对象声明和定义,可传递给子类,子类的子类等
EmployeeCON *pEmployeeCON;
};
#endif // MANAGERCON_H
managercon.cpp
#include "managercon.h"
//全局回调函数
void g_callback_receive(char *data, int nb_data ,void *obj) {
if (obj == nullptr)
return;
ChwContext *contx = (ChwContext*) obj;
ManagerCON *mc = (ManagerCON*) contx->user_callback;
std::string tmpstr(data,nb_data);
mc->callback_receive(tmpstr);
}
ManagerCON::ManagerCON()
{
p_ChwContext = new ChwContext;
p_ChwContext->receiveQt = g_callback_receive;
p_ChwContext->user_callback = this;
pEmployeeCON = new EmployeeCON(p_ChwContext);
}
ManagerCON::~ManagerCON()
{
safe_delete(pEmployeeCON);
safe_delete(p_ChwContext);
}
void ManagerCON::sendwork(int count)
{
pEmployeeCON->dowork(count);
}
void ManagerCON::callback_receive(std::string msg)
{
printf("msg=%s\n",msg.c_str());
}
调用测试
ManagerCON mManagerCON;
mManagerCON.sendwork(10);
打印
msg=total:450
C++11新特性td::tr1::function和std::tr1::bind实现回调函数
关于新特性参考:C++ std::tr1::function和std::tr1::bind模板类介绍,qt测试,这里不多说,直接上代码。
managerbind.h
#ifndef MANAGERBIND_H
#define MANAGERBIND_H
#include <iostream>
#include <tr1/functional>
#include <string>
class employeeBind{
//员工类
public:
employeeBind(){
}
void dowork(int count){
int sum = 0;
for(int index=0;index<count;index++)
sum += index*10;
std::string msg = "total:" + std::to_string(sum);
if(Callback)
Callback(msg);//回调函数
}
std::tr1::function<void(std::string)> Callback;
};
class ManagerBind//经理类
{
public:
ManagerBind();
employeeBind memployeeBind;
void sendwork(int num);
void callbackBD(std::string msg);
};
#endif // MANAGERBIND_H
managerbind.cpp
#include "managerbind.h"
ManagerBind::ManagerBind()
{
memployeeBind.Callback = std::tr1::bind(&ManagerBind::callbackBD,this, std::tr1::placeholders::_1);
}
void ManagerBind::sendwork(int num){
memployeeBind.dowork(num);
}
void ManagerBind::callbackBD(std::string msg)
{
printf("msg=%s\n",msg.c_str());
}
调用测试
ManagerBind mManagerBind;
mManagerBind.sendwork(10);
打印
msg=total:450
C++联合体union实现回调函数
union也叫联合体,在一个“联合体”内可以定义多种不同数据类型的变量,这些变量共享同一段内存,存储在内存中的起始地址相同,使用了覆盖计数,以达到节省空间的目的。union变量所占用的内存长度等于最长的成员变量的内存长度。
union的其中一个作用是实现简洁的线程回调函数。
managerunion.h
#ifndef MANAGERUNION_H
#define MANAGERUNION_H
#include <pthread.h>
#include <iostream>
#include <string>
class ManagerUnion;
//定义函数指针数据类型
typedef void* (ManagerUnion::*ThreadMeth)(void *);
typedef void* (*pThreadMethod)(void *);
class ManagerUnion
{
public:
ManagerUnion();
void startThread();
void callbackUM(void *);
int count;
pthread_t m_threadID;
};
#endif // MANAGERUNION_H
managerunion.cpp
#include "managerunion.h"
ManagerUnion::ManagerUnion()
{
count = 502;
printf("ManagerUnion::ManagerUnion tid = %lu\n", pthread_self());
}
void ManagerUnion::startThread()
{
union{
//两个函数指针共享同一段内存
pThreadMethod pFunc;
void* (ManagerUnion::*ThreadMeth)(void *);
}obj;
obj.ThreadMeth = (ThreadMeth)&ManagerUnion::callbackUM;
pthread_create(&m_threadID,0,obj.pFunc,this);//创建线程
}
//回调函数,也是线程处理函数,在子线程运行,调用的还是主类的函数
void ManagerUnion::callbackUM(void *)
{
printf("ManagerUnion::callbackUM tid = %lu\n", pthread_self());
printf("count=%d\n",count);
}
调用测试
ManagerUnion mManagerUnion;
mManagerUnion.startThread();
打印
ManagerUnion::ManagerUnion tid = 139898503038848
ManagerUnion::callbackUM tid = 139898191017728
count=502