一、回调函数
回调函数就是一个通过函数指针调用的函数。A类将一个函数的地址作为参数传给B类的一个函数,此时A类就不用关心这个函数的执行了。当B类里面触发了某个特定的条件,B类调用这个回调函数,进行响应。
回调函数分为两种,一种是同步回调函数,另一种是异步回调函数。两种回调都可以使得调用者不再依赖于被调用者,使得二者在代码空间分布上解耦,而异步回调函数更是在运行时从时间上将二者解耦。
二、回调函数的例子
(1)主窗体
//MainWindow.h
public:
//静态方法的回调函数
static void setPos(MainWindow *m, const QPointF &r);
void setLinePos(const QPointF &r);
//MainWindow.cpp------------------------------------------------------
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
//widget提升为了子部件
//把对象实例的指针或引用做为参数传给子部件。就能找到主窗口中的控件。
ui->widget->setCallbackFunc(this, setPos);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::setPos(MainWindow *m, const QPointF &r)
{
//静态函数中,没有this指针,因此需要传当前对象,否则无法拿到lineEdit控件
m->setLinePos(r);
}
void MainWindow::setLinePos(const QPointF &r)
{
ui->lineEdit1->setText(QString::number(r.x()));
ui->lineEdit2->setText(QString::number(r.y()));
}
(2)子部件
//Call.h
public:
explicit Call(QWidget *parent = nullptr);
~Call();
void setCallbackFunc(MainWindow *m, void(*func) (MainWindow *m, const QPointF &point));
protected:
void paintEvent(QPaintEvent *event);
void keyPressEvent(QKeyEvent *event);
private:
void(*m_callbackFunc)(MainWindow *m, const QPointF &point);
MainWindow *m_m;
//Call.cpp-----------------------------------------------------
Call::Call(QWidget *parent)
:QWidget(parent)
{
setFocusPolicy(Qt::StrongFocus);
}
Call::~Call()
{
}
void Call::setCallbackFunc(MainWindow *m,void(*func) (MainWindow *m, const QPointF &point))
{
//保存回调函数地址,以及主窗口对象
m_m = m;
m_callbackFunc = func;
}
void Call::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event);
//想要绘图生效,painter必须关联到画图设备,当前代码中画图设备为this(当前窗口)
QPainter painter(this);
painter.setPen(Qt::white);
painter.setBrush(Qt::red);
painter.drawRect(0, 0, 50, 50);
}
void Call::keyPressEvent(QKeyEvent *event)
{
//获取键盘事件,事件触发后,进行移动
switch(event->key())
{
case Qt::Key_Up:
move(this->x(), this->y() - 20);
break;
case Qt::Key_Down:
move(this->x(), this->y() + 20);
break;
case Qt::Key_Left:
move(this->x() - 20, this->y());
break;
case Qt::Key_Right:
move(this->x() + 20, this->y());
break;
default:
break;
}
//子部件调用回调函数,将此时的部件坐标告诉对应的主窗口
m_callbackFunc(m_m, this->pos());
}
三、类内成员函数作为回调函数为什么需要加static?
在类中封装回调函数,回调函数必须要加上static关键字,这样this指针就不能用了,类中静态函数只能访问类的静态成员,不能访问类中的非静态成员,静态函数属于这个类,而不再仅仅属于具体的对象,类的静态函数中没有默认的this指针。但是为什么要这要做呢?
//1.例如一个回调函数被要求声明为以下形式(不属于具体的对象):
void CALLBACK function();
//2.如果这个函数在类ObjClass里面,编译器会为其添加一个this指针,
//用于指向调用该函数的对象。所以编译出来的代码是这种形式:
void CALLBACK ObjClass::function(ObjClass* this)
{}
函数参数列表与被要求声明的形式不一致。就比如说,主窗口中回调函数中有this指针,但是子部件调用的时候可没有,导致参数不匹配。因此我们应该废弃this,而我们也恰巧需要使用主窗口类中的控件,我们可以把对象实例的指针或引用做为参数,和回调函数地址一并传给子部件。
备注:回调函数也可以声明在类外,全局的传类对象指针给回调函数即可使用类内成员,但是回调函数是全局的,所以会影响类的封装性
四、回调函数存在的信任问题
Node.js 天然的异步特性设计使得大多数接口都是异步的,自然也充满了各种各样的异步回调函数。在 Node.js 里,回调函数最大的问题不仅仅是代码书写上产生了 callback hell,更本质的问题是回调函数的调用得不到有效的控制。因为外部异步接口不都像框架一样是基本可信任的,所以你并不能保证别人会如何对待你传入的回调函数。例如调用者疯狂调用回调函数。有趣的是,异步流程控制协议 Promise 再一次利用控制反转解决了回调函数调用的「信任」问题。Promise 把本来由异步接口控制的回调函数调用权收归自己所有,所有的异步调用都是通过 Promise 这个「中介」来完成,而回调函数的管理和调用也由 Promise 来一手掌握。
参考各位大佬的文章,再次感谢!
百度百科:回调函数
喜欢吃冰棍de谷利文君:Qt错误:Painter not active
菌丶:类中的回调函数要加static的原因
云端的蜗牛:C++回调函数必须为静态函数的原因
lesliefish:在Qt中使用回调函数替代信号槽
一只代码狗的自我修养:完全理解回调函数