通过Lambda和递归函数调用了解QTimer

时间:2014-11-03 11:59:54

标签: c++ qt c++11 lambda qtcore

我有以下代码:

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);
}

使用连接到lambda函数的timout创建单次计时器,该函数每秒记录lambda函数的入口(将文本打印到stdout)并再次调用该函数。

这没有问题。但是,如果我删除对deleteLater的连接调用(在lambda函数下面),但在lambda函数中启用deleteLater调用,则该函数将失败。它打印一次,不久之后,在尝试删除计时器对象时崩溃。

此实例中两个deleteLater调用之间的区别是什么以及为什么将deleteLater置于lambda函数中会导致此处出现问题,而创建单独的连接会按预期工作,即使两者都调用deleteLater以响应Timer& #39;超时信号?

2 个答案:

答案 0 :(得分:9)

鉴于没有拼写错误或某些我不知道的信息,我认为原因是因为您试图稍后删除class实例而不是分配的QTimer实例上述方法中的堆。

如果您查看非lambda版本,则会调用deleteLater上的QTimer instance,因为它是连接调用中的接收者。

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

但是,在lambda变体中,未捕获计时器实例,并且自然地,在其当前版本中将不会分别访问它。为了使两个备选方案等效,需要对代码进行此修改:

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

答案 1 :(得分:0)

默认方法不应该是手动内存管理。 Qt可以自动管理计时器生命周期。在Qt 5.4及更高版本中,代码变得非常简单,可以是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();
   });
}