QtMiddleExperience

Какие ошибки делают команды, когда используют 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

Related topics