[Qt]使用 QTimer::singleShot 导致的一次程序崩溃

代码

cpp 复制代码
void Widget::on_pushButton_clicked()
{
    QString str = "sing jump rap";
    QTimer::singleShot(5000, this, [&](){
        str = QStringLiteral("唱 跳 rap");
        qDebug() << "str : " << str;
    });
}

简单推测就是 QTimer::singleShot 当定时到了时,访问了已经释放的对象导致的。

思考

QTimer::singleShot 是如何实现的

cpp 复制代码
void QTimer::singleShotImpl(int msec, Qt::TimerType timerType,
                            const QObject *receiver,
                            QtPrivate::QSlotObjectBase *slotObj)
{
    if (msec == 0) {
        bool deleteReceiver = false;
        // Optimize: set a receiver context when none is given, such that we can use
        // QMetaObject::invokeMethod which is more efficient than going through a timer.
        // We need a QObject living in the current thread. But the QThread itself lives
        // in a different thread - with the exception of the main QThread which lives in
        // itself. And QThread::currentThread() is among the few QObjects we know that will
        // most certainly be there. Note that one can actually call singleShot before the
        // QApplication is created!
        if (!receiver && QThread::currentThread() == QCoreApplicationPrivate::mainThread()) {
            // reuse main thread as context object
            receiver = QThread::currentThread();
        } else if (!receiver) {
            // Create a receiver context object on-demand. According to the benchmarks,
            // this is still more efficient than going through a timer.
            receiver = new QObject;
            deleteReceiver = true;
        }
        QMetaObject::invokeMethodImpl(const_cast<QObject *>(receiver), slotObj,
                                      Qt::QueuedConnection, nullptr);
        if (deleteReceiver)
            const_cast<QObject *>(receiver)->deleteLater();
        return;
    }
    new QSingleShotTimer(msec, timerType, receiver, slotObj);
}

可见当 msec 不为零时,会创建一个 QSingleShotTimer 的对象,既然是 new 一个对象,那么这个对象是什么时候删除的,

cpp 复制代码
void QSingleShotTimer::timerEvent(QTimerEvent *)
{
    // need to kill the timer _before_ we emit timeout() in case the
    // slot connected to timeout calls processEvents()
    if (timerId > 0)
        killTimer(timerId);
    timerId = -1;
    if (slotObj) {
        // If the receiver was destroyed, skip this part
        if (Q_LIKELY(!receiver.isNull() || !hasValidReceiver)) {
            // We allocate only the return type - we previously checked the function had
            // no arguments.
            void *args[1] = { nullptr };
            slotObj->call(const_cast<QObject*>(receiver.data()), args);
        }
    } else {
        emit timeout();
    }
    // we would like to use delete later here, but it feels like a
    // waste to post a new event to handle this event, so we just unset the flag
    // and explicitly delete...
    qDeleteInEventHandler(this); //里面就是 delete this
}

看起来好像就是在这里释放了自己

相关推荐
郝学胜-神的一滴2 小时前
Qt 高级编程 045:坐标体系深度实战
开发语言·c++·windows·python·qt·程序人生
爱吃巧克力的程序媛4 小时前
main.cpp注册 C++ 类到 QML 元对象系统
c++·qt
励志不掉头发的内向程序员8 小时前
【LibreCAD 2D架构】从鼠标点击到图形创建:RS_ActionDrawLine交互流程与状态机解析
开发语言·c++·qt·学习·系统架构·计算机外设·交互
尘中远9 小时前
给C++工业软件搭建 Agent
开发语言·c++·qt·ai·agent
Quz11 小时前
QML Page:把 Page 装进切换容器里
qt
秋田君11 小时前
Qt_串口编程
开发语言·qt
励志不掉头发的内向程序员12 小时前
【LibreCAD 2D架构】从两个坐标到图形实体:RS_ActionDrawLine如何创建RS_Line
开发语言·c++·qt·学习·系统架构
似水এ᭄往昔13 小时前
【Qt】--常用控件(按钮类控件)
开发语言·qt
_林枭_1 天前
ZW3D二次开发_原生表单_如何设置表单左上角图标
qt·zw3d
Quz1 天前
QML 快捷键篇:基础快捷键与组合键序列
qt·qml