回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应
std::function作为回调函数
std::function配合std::bind和lambda表达式能够很方便的指向函数指针,下面通过代码介绍作为回调函数的一个使用场景
现有类MainWindow、类ModuleA和类ModuleB
其中MainWindow调用ModuleA和ModuleB。
一、正常的结构关系是MainWindow创建ModuleB对象,MainWindow调用ModuleB,当ModuleB中想调用MainWindow中的成员变量或者函数的时候,为了保持MainWindow和ModuleB的结构关系,我们可以使用回调函数来达到这个目的
MainWindow调用代码
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_pModuleB = new ModuleB();
m_pModuleB->bindFun1(std::bind(&MainWindow::count, this));
}
int MainWindow::count()
{
qDebug() << __FUNCTION__ << "song" << m_nCount;
return ++m_nCount;
}
void MainWindow::on_pushButton_clicked()
{
m_pModuleB->start();
}
ModuleB的实现代码
#include <functional>
class ModuleB
{
public:
ModuleB();
void start();
void bindFun1(const std::function<int ()> &fun1);
private:
std::function<int ()> m_fun1;
int m_nSize = 0;
};
ModuleB::ModuleB()
{
}
void ModuleB::start()
{
m_nSize = m_fun1();
m_nSize++;
qDebug() << __FUNCTION__ << "song" << m_nSize;
}
void ModuleB::bindFun1(const std::function<int ()> &fun1)
{
m_fun1 = fun1;
}
我们可以看到,MainWindow调用ModuleB的start函数,start函数中通过函数指针的方式调用MainWindow中的count函数,这就是回调,m_fun1就是回调函数。其中一个关键的地方就是绑定,通过std::bind把MainWindow成员函数的地址传给m_fun1。
运行结果
二、再看另外一种情况,ModuleB想调用MudleA中的功能,假设ModuleB和MudleA本身是互相独立的模块,为了保证代码的低耦合,ModuleB不能直接包含ModuleA进行相关功能的调用。这时使用回调的方式我们也能实现,需要通过中间类MainWIndow调用ModlueA的功能
直接看代码
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_pModuleA = new ModuleA();
m_pModuleB = new ModuleB();
m_pModuleB->bindFun1(std::bind(&MainWindow::count, this));
m_pModuleB->bindFun2([this](int i){
return m_pModuleA->doWork(m_nCount, i);
});
}
int MainWindow::count()
{
qDebug() << __FUNCTION__ << "song" << m_nCount;
return ++m_nCount;
}
void MainWindow::on_pushButton_clicked()
{
m_pModuleB->start();
}
void MainWindow::on_pushButton_2_clicked()
{
m_pModuleB->startB();
}
#include <functional>
class ModuleB
{
public:
ModuleB();
void start();
void startB();
void bindFun1(const std::function<int ()> &fun1);
void bindFun2(const std::function<int (int)> &fun2);
private:
std::function<int ()> m_fun1;
std::function<int (int)> m_fun2;
int m_nSize = 0;
};
ModuleB::ModuleB()
{
}
void ModuleB::start()
{
m_nSize = m_fun1();
m_nSize++;
qDebug() << __FUNCTION__ << "song" << m_nSize;
}
void ModuleB::startB()
{
int size = m_fun2(m_nSize);
qDebug() << __FUNCTION__ << "song" << size;
}
void ModuleB::bindFun1(const std::function<int ()> &fun1)
{
m_fun1 = fun1;
}
void ModuleB::bindFun2(const std::function<int (int)> &fun2)
{
m_fun2 = fun2;
}
ModuleA::ModuleA()
{
}
int ModuleA::doWork(int i, int j)
{
return i + j;
}
可以看到,调用startB函数的时候,会回调MainWindow中的一个匿名函数,匿名函数的主要功能就是ModuleA的功能。这种情况m_fun2绑定的是一个匿名函数,std::function作为回调函数是真的很方便
先运行start再运行startB的结果
上面这两种情况的处理方式,使得代码的耦合度变低,代码结构很清晰,就不会互相包含的情况。std::function、lambda、std::bind作为C++11的新特性,实用性很强,使用场景需进一步挖掘。