问题
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