Qt的lambda槽函数的注意事项

背景

今天现场反馈了一个软件崩溃的bug,这个bug造成的异常居然没被捕获生成dump文件,很奇怪。但是种种努力复现了这个bug。简单记录一下。

实验

假设有这么两个类

ClassA

ClassA.h
#ifndef CLASSA_H
#define CLASSA_H

#include <QObject>

class ClassA : public QObject
{
    Q_OBJECT
public:
    explicit ClassA(QObject *parent = nullptr);

signals:
    void aboutToRemove();

};

#endif // CLASSA_H
ClassA.cpp
#include "classa.h"

ClassA::ClassA(QObject *parent) : QObject(parent)
{

}

ClassB

ClassB.h
#ifndef CLASSB_H
#define CLASSB_H

#include "classa.h"

class ClassB : public QObject
{
    Q_OBJECT
public:
    explicit ClassB(QObject *parent = nullptr);
    ~ClassB();

    void setClassA(ClassA* a);

signals:

private:
    ClassA *mA;
};

#endif // CLASSB_H
ClassB.cpp
#include "classb.h"

#include "classa.h"

#include <QDebug>
#include <QDateTime>

ClassB::ClassB(QObject *parent) : QObject(parent)
{
    mA = nullptr;
}

ClassB::~ClassB()
{
    qDebug() << "ClassB::~ClassB()";
}

void ClassB::setClassA(ClassA *a)
{
   mA = a;

   connect(mA, &ClassA::aboutToRemove, [&](){
//   connect(mA, &ClassA::aboutToRemove, this, [&](){
       qDebug() << QDateTime::currentDateTime() << "about to remove";
       mA->disconnect(this);
       mA = nullptr;
   });
}

然后在函数中调用
main.cpp

int main(int argc, char *argv[])
{
    ...
    
    ClassA *clsA = new ClassA();
    ClassB *clsB = new ClassB();

    clsB->setClassA(clsA);

    delete clsB;
    clsB = nullptr;

    clsA->aboutToRemove();
    
    ...
}

现象

1.直接运行上面的代码
输出

ClassB::~ClassB()
QDateTime(2022-10-14 22:00:58.039 中国标准时间 Qt::LocalTime) about to remove

2.修改后再运行
在这里插入图片描述
此时输出:

ClassB::~ClassB()

分析

lambda函数作为槽函数时,有两种写法

[static] QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)

[static] QMetaObject::Connection QObject::connect(const QObject *sender, PointerToMemberFunction signal, const QObject *context, Functor functor, Qt::ConnectionType type = Qt::AutoConnection)

主要区别在于第二个写法指定了一个context,这个context和sender联合保证了这条connection的有效期。
在这里插入图片描述因此,在前面的实验中,第一次用的lambda槽函数是不带context,即使delete了clsB,这个connection依然还是存在,所以信号触发时,lambda函数会被继续执行。
第二次用的是带context的,contex(clsB)被析构时,这个connection也就被断开了,lambda函数就不会被执行了。

结论

在使用lambda作为槽函数时,最好加上个context,否则就要百分百确保信号发生时,lambda函数内的变量都是可用的。
在这里插入图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值