Understanding QTimer with Lambda and recursive function call

戏子无情 提交于 2019-12-19 18:20:33

问题


I have the following code:

void class::Testfunc()
{
    QTimer* timer = new QTimer;
    QObject::connect(timer, &QTimer::timeout, [this](){
        emit Log("Time out...");
        TestFunc(serverAddress, requestsFolderPath);
       // deleteLater(); //*** why does this crash if used to replace the connect below?
    });
    connect(timer, &QTimer::timeout, timer, &QTimer::deleteLater);
    timer->setSingleShot(true);
    timer->start(1000);
}

A single shot timer is created with a timout connected to a lambda function that logs the entrance to the lambda function each second (prints text to stdout) and calls the function again.

This works without issue. However, if I remove the connect call to deleteLater (below the lambda function), but enable the deleteLater call in the lambda function, the function fails. It prints once and shortly after, crashes in trying to delete the timer object.

What is the difference between the two deleteLater calls in this instance and why would placing the deleteLater in the lambda function cause a problem here, whereas creating a separate connection works as expected, even though both are calling deleteLater in response to the Timer's timeout signal?


回答1:


Given that there is no typo or some information that I am not aware of, I think the reason is that because you are trying to delete your class instance later rather than the QTimer instance allocated on the heap in the aforementioned method.

If you take a look at the the non-lambda version, that calls the deleteLater on the QTimer instance as that is the receiver in the connect call.

connect(timer, &QTimer::timeout, timer, &QTimer::deleteLater);

However, in the lambda variant, the timer instance is not captured and naturally, there would be no access to it in its current version, respectively. To make the two alternatives equivalent, this modification needs to be done to the code:

QObject::connect(timer, &QTimer::timeout, [this, timer](){
//                                               ^^^^^
    emit Log("Time out...");
    TestFunc(serverAddress, requestsFolderPath);
    timer->deleteLater();
//  ^^^^^^^
});



回答2:


The default approach should be not to do manual memory management. Qt can manage timer lifetime automatically. The code becomes quite simple in Qt 5.4 and later, and can be backported for Qt 5.0-5.3:

// https://github.com/KubaO/stackoverflown/tree/master/questions/qtimer-retrofit-26713879
#include <QtCore>

struct Class : QObject {
   void TestFunc();
   void Log(const char *str) { qDebug() << str; }
};

#if QT_VERSION >= QT_VERSION_CHECK(5,4,0)
namespace compat { using QT_PREPEND_NAMESPACE(QTimer); }
#else
QT_BEGIN_NAMESPACE
Q_CORE_EXPORT void qDeleteInEventHandler(QObject *o);
QT_END_NAMESPACE
namespace compat {
using QT_PREPEND_NAMESPACE(qDeleteInEventHandler);
template <class Fun> struct SingleShotHelper : QObject, Fun {
   QBasicTimer timer;
   template <class F> SingleShotHelper(int msec, QObject *context, F &&fun) :
      QObject(context ? context : QAbstractEventDispatcher::instance()),
      Fun(std::forward<F>(fun)) {
      timer.start(msec, this);
      if (!context)
         connect(qApp, &QCoreApplication::aboutToQuit, this, &QObject::deleteLater);
   }
   void timerEvent(QTimerEvent *ev) override {
      if (ev->timerId() != timer.timerId()) return;
      timer.stop();
      (*this)();
      qDeleteInEventHandler(this);
   }
};
using Q_QTimer = QT_PREPEND_NAMESPACE(QTimer);
class QTimer : public Q_QTimer {
   Q_OBJECT
public:
   QTimer(QObject *parent = {}) : Q_QTimer(parent) {} // C++17: using Q_QTimer::Q_QTimer;
   template <class Fun>
   inline static void singleShot(int msec, QObject *context, Fun &&fun) {
      new SingleShotHelper<Fun>(msec, context, std::forward<Fun>(fun));
   }
}; }
#endif

void Class::TestFunc() {
   compat::QTimer::singleShot(1000, this, [this]{
      emit Log("Timeout...");
      TestFunc();
   });
}


来源:https://stackoverflow.com/questions/26713879/understanding-qtimer-with-lambda-and-recursive-function-call

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!