[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
}

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

相关推荐
残醉1 天前
QT的拖拽功能
qt
蒋星熠1 天前
QT项目 -仿QQ音乐的音乐播放器(第五节)
开发语言·qt
冬停1 天前
解决 Qt 网络 SSL 报错:`cannot call unresolved function` 的终极指南
网络·qt·ssl
mct1232 天前
QUdpSocket发送组播和接受组播数据
qt·组播
火山上的企鹅2 天前
Qt Quick 之动态旋转刻度盘(无人机中指南针 Demo )
qt·无人机·cocos2d
是小峰呀2 天前
QT+opencv+yolov8推理
开发语言·qt·yolo
GUIQU.2 天前
【QT】概述
开发语言·c++·qt
茉莉玫瑰花茶2 天前
Qt 常用控件 - 5
开发语言·qt
残醉2 天前
QT自定义控件
qt
啊森要自信2 天前
【QT】常⽤控件详解(四)常用显示类控件类 Label && LCDNumber && ProgressBar && Calendar Widget
开发语言·数据库·c++·qt·qt6.3