Какие ошибки делают команды, когда используют Qt без понимания его модели выполнения или сборки?
Самые частые ошибки: вызов методов QObject из чужого треда, забытый Q_OBJECT макрос, блокировка event loop в главном треде и сборка без rebuild после изменений .ui/.qrc-файлов.
Типичные командные ошибки при работе с Qt
1. Нарушение thread affinity
Самая частая причина крашей в production — вызов методов QObject напрямую из другого треда вместо использования сигналов или QMetaObject::invokeMethod.
// НЕПРАВИЛЬНО: прямой вызов из рабочего треда
void WorkerThread::run() {
mainWindowPtr->updateLabel("done"); // UB, краш
}
// ПРАВИЛЬНО: через очередь событий
void WorkerThread::run() {
emit workFinished("done"); // маршрутизируется в главный тред
// или:
QMetaObject::invokeMethod(mainWindowPtr, "updateLabel",
Qt::QueuedConnection,
Q_ARG(QString, "done"));
}
2. Забытый Q_OBJECT
Если подкласс QObject не имеет Q_OBJECT в private-секции, сигналы и слоты не работают, qobject_cast возвращает nullptr, свойства не видны из QML. Компилятор не предупреждает — MOC просто не генерирует метаданные.
3. Блокировка event loop
Тяжёлые операции в слоте главного треда (I/O, вычисления > 16ms) замораживают GUI и ломают таймеры:
// НЕПРАВИЛЬНО: блокировка на 2 секунды
void MainWindow::onButtonClicked() {
QThread::sleep(2); // GUI заморожен
}
// ПРАВИЛЬНО: вынести в worker thread
void MainWindow::onButtonClicked() {
auto *worker = new QThread(this);
connect(worker, &QThread::started, this, [this]() {
// тяжёлая работа здесь
});
worker->start();
}
4. Не пересобирают после изменений MOC-зависимых файлов
Изменение .ui, .qrc, добавление нового сигнала в .h требует пересборки — make не всегда это обнаруживает. Команды запускают старый бинарник и удивляются, что сигнал не работает. Решение: make clean && make или настроить инкрементальную сборку через CMake с AUTOMOC ON.
5. Смешение raw pointer и QObject parent ownership
Удаление объекта, у которого есть Qt-parent, через delete вместо deleteLater или доверие parent-у — двойное удаление:
QWidget *parent = new QWidget;
QLabel *label = new QLabel(parent); // parent владеет label
delete label; // OK, но parent попытается удалить снова!
// Лучше: label->setParent(nullptr); delete label;
// Или: просто не вызывать delete — parent сделает это
6. Игнорирование Qt::ConnectionType при connect
Использование Qt::DirectConnection для кросс-тредных сигналов без осознания последствий — слот выполняется в треде sender-а, не receiver-а.
7. Неправильная работа с QML и C++ типами
Передача неregistered типов в QML через сигналы ведёт к undefined в QML. Все кастомные типы должны быть зарегистрированы через qmlRegisterType или QML_ELEMENT.
Подводные камни
- CMake
target_link_librariesбезQt6::Coreдаёт линковочные ошибки, которые выглядят как «Qt не установлен». AUTOMOCв CMake не обрабатывает .cpp-файлы с Q_OBJECT — только .h. Нужно явно добавить файл вset_source_files_properties.- QTimer в non-GUI треде без event loop никогда не срабатывает — тихая ошибка.
- Создание QApplication после форка (в daemon-процессах) ломает X11-соединение.
- Qt Quick Controls 2 и Qt Quick Controls 1 нельзя смешивать в одном приложении — конфликт QML module namespace.
- Отсутствие
Q_DECLARE_METATYPEдля кастомного типа вQVariantприводит к silent data loss.
What hurts your answer
- Перечислять ошибки без объяснения причин
- Не отличать beginner mistakes от production failure modes
- Не предлагать процесс, который предотвращает повторение ошибок
What they're listening for
- Знает типичные ошибки при работе с Qt
- Понимает причины ошибок
- Предлагает практики prevention и early detection