我有以下代码:
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;超时信号?
答案 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();
});
}