Ответ 1
Следующие строки функционально эквивалентны:
QTimer::singleShot(0, object, &Class::funcA); // Qt 5
QTimer::singleShot(0, object, SLOT(funcA())); // Qt 4
QMetaObject::invokeMethod(object, "funcA", Qt::QueuedConnection);
Как теперь очевидно, целью является выполнение вызова в цикле событий. Вызываемый в очередь вызов приводит к отправке QMetaCallEvent
в object
. Это событие обрабатывается QObject::event
и приводит к вызову требуемого метода. Таким образом, следующие эквиваленты эквивалентны, даже если последняя представляет собой частную деталь реализации - позволяя мне пропустить информацию о создании события:
QMetaObject::invokeMethod(object, "funcA", Qt::QueuedConnection);
QCoreApplication::postEvent(object, new QMetaCallEvent{...});
Это удобно в различных ситуациях. Например:
-
Чтобы выполнить некоторый код после того, как все ранее опубликованные события были обработаны.
-
Выполняется только после запуска цикла событий.
-
Чтобы вызвать invokable-метод, недоступный из-за модификаторов доступа С++. Вызываемые методы: сигналы, слот и объявленные методы
Q_INVOKABLE
. -
Прямой вызов небезопасен (прочитайте: ошибка!), когда
QObject
находится в другом потоке, если вы явно не вызываете метод, документированный как потокобезопасный.
Приостановленный вызов является необходимостью, если вы хотите, чтобы цикл цикла немедленно завершался: прямой вызов quit()
является no-op, если цикл еще не запущен.
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
app.quit(); // this is a no-op since the event loop isn't running yet
return app.exec(); // will not quit as desired
}
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
QMetaObject::invokeMethod(&app, "quit", Qt::QueuedConnection);
return app.exec(); // will return immediately
}
В идеале вы бы использовали postToThread
из этого ответа, он предлагает самый дешевый способ вызова методов в других потоках:
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
postToThread([]{ qApp->quit(); });
}
Альтернативный способ сделать это - использовать QObject
в качестве источника сигнала:
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
{
QObject src;
src.connect(&src, &QObject::destroyed, &app, &QCoreApplication::quit,
Qt::QueuedConnection);
}
return app.exec(); // will return immediately
}
Еще один способ - использовать пользовательское событие и действовать в его деструкторе:
int main(int argc, char ** argv) {
QCoreApplication app{argc, argv};
struct QuitEvent : QEvent {
QuitEvent() : QEvent(QEvent::None) {}
~QuitEvent() { qApp->quit(); }
};
QCoreApplication::postEvent(&app, new QuitEvent);
return app.exec(); // will return immediately
}