Как соединять signals и slots в Qt 5 vs Qt 6 (синтаксис нового стиля)?
Новый синтаксис Qt 5/6 использует указатели на методы (&QPushButton::clicked), даёт проверку типов при компиляции и поддерживает лямбды с контекстным объектом для безопасного отключения.
Синтаксис connect() в Qt 5 и Qt 6
Qt предоставляет два способа соединения сигналов и слотов. Старый синтаксис использует строковые макросы SIGNAL() и SLOT(), новый — указатели на функции-члены. В Qt 5 появился новый стиль, Qt 6 его унаследовал и сделал предпочтительным.
Старый синтаксис (Qt 4 / совместимый)
#include <QObject>
#include <QPushButton>
#include <QApplication>
// Старый стиль — строки, проверка только в runtime
QObject::connect(
button, SIGNAL(clicked(bool)),
this, SLOT(onClicked(bool))
);
Ошибки в именах сигнала или слота проявятся только во время выполнения (сообщение в консоль), не при компиляции. Сигнатуры должны совпадать посимвольно.
Новый синтаксис (Qt 5+, рекомендован в Qt 6)
#include <QObject>
#include <QPushButton>
#include <QApplication>
// Новый стиль — указатели на методы, проверка при компиляции
QObject::connect(
button, &QPushButton::clicked,
this, &MyWidget::onClicked
);
// С лямбдой (удобно для коротких обработчиков)
QObject::connect(button, &QPushButton::clicked, [this](bool checked) {
qDebug() << "Button clicked, checked:" << checked;
});
Разрешение перегруженных сигналов
Если сигнал перегружен (например, QSpinBox::valueChanged существует в вариантах int и QString), нужно явно указать тип:
// Qt 5 — qOverload (C++14)
connect(
spinBox, qOverload<int>(&QSpinBox::valueChanged),
this, &MyWidget::handleInt
);
// Qt 6 — тот же qOverload, но также можно использовать статический каст
connect(
spinBox,
static_cast<void (QSpinBox::*)(int)>(&QSpinBox::valueChanged),
this,
&MyWidget::handleInt
);
Qt 6: новые возможности connect()
В Qt 6 добавлен параметр контекста (context object) для лямбд, чтобы автоматически отключать соединение при удалении объекта-приёмника:
// context = this: лямбда не будет вызвана после удаления this
connect(
sender, &Sender::dataReady,
this, // context object
[this](const QByteArray &data) {
processData(data);
}
);
Отключение (disconnect)
// Отключить конкретное соединение (по возвращённому QMetaObject::Connection)
auto conn = connect(src, &Src::sig, dst, &Dst::slot);
disconnect(conn);
// Отключить все соединения сигнала
disconnect(src, &Src::sig, nullptr, nullptr);
Подводные камни
- SIGNAL/SLOT и пробелы: в старом синтаксисе лишний пробел в сигнатуре (
SIGNAL(clicked( bool ))) вызывает сбой соединения — строки сравниваются побуквенно после нормализации, но ошибку легко пропустить. - Перегруженные сигналы без qOverload: без явного каста компилятор выдаёт неочевидную ошибку «cannot resolve overloaded function».
- Лямбда без контекста: если объект удалён, а лямбда ещё хранит
this, возникает dangling pointer. Всегда передавайте контекстный объект третьим параметром. - Qt::ConnectionType: по умолчанию
Qt::AutoConnection— между потоками становитсяQueuedConnection. Ошибочное использованиеDirectConnectionмежду потоками вызывает data race. - Дублирующиеся соединения: повторный вызов
connect()с теми же аргументами создаёт ещё одно соединение — слот вызовется дважды. ИспользуйтеQt::UniqueConnectionили проверяйте возвращаемыйQMetaObject::Connection. - Q_OBJECT отсутствует: если в классе нет макроса
Q_OBJECT, moc не генерирует метаданные, и сигналы/слоты молча не работают. - Удаление объекта в слоте: удаление
senderвнутри слота, вызванного этим же sender, — неопределённое поведение. ИспользуйтеQObject::deleteLater().
Common mistakes
- Объяснять connect syntax Qt 5 и Qt 6 только по синтаксису, без жизненного цикла и стоимости.
- Игнорировать ошибки, null/empty состояния, порядок инициализации или режим сборки.
- Давать пример, который работает в демо, но ломается при изменении владельца ресурса.
- Смешивать signals/slots с generic callbacks и забывать про thread affinity.
What the interviewer is testing
- Кандидат формулирует точную модель для connect syntax Qt 5 и Qt 6, а не только определение.
- Пример компилируем, безопасен по lifetime и соответствует версии технологии.
- Названы trade-off, ограничения и диагностируемые симптомы ошибки.
- Понимает границу между C++ кодом, runtime/framework metadata и editor/UI слоем.