Использование сигналов Qt и слотов против вызова метода напрямую

допустим, у меня есть главное окно со слайдером и виджетом внутри этого окна с помощью метода setValue(int). Я хочу вызвать этот метод каждый раз, когда значение ползунка изменилось.

есть ли практическая разница между двумя следующими способами достижения этого:

1

void MainWindow::on_slider_valueChanged(int value)
{
    ui->widget->setValue(value);
}

2

// somewhere in constructor
connect(ui->slider, SIGNAL(valueChanged(int)), ui->widget, SLOT(setValue(int)));

для меня первый подход выглядит лучше, потому что он, возможно, избегает некоторых накладных расходов, связанных с сигналами и механизмами слотов и кроме того, позволяет мне обрабатывать value перед отправкой widget, если в этом есть необходимость.

существуют ли сценарии, в которых второе решение лучше?

5 ответов


основное отличие в вашем примере использования сигнала вместо прямого вызова состоит в том, чтобы разрешить более одного слушателя.

Если вы напрямую вызываете свой виджет setValue (), то только один виджет получит сигнал C++.

Если вы используете сигнал Qt, теперь любой другой объект может подключиться для получения события всякий раз, когда оно происходит.

Если вы не предвидите, что какой-либо другой объект когда-либо захочет получить значение по сигналу, я бы не стал беспокоиться об этом. Ля прямой вызов определенно намного быстрее (между инструкциями 3 и 6 CPU вместо того, чтобы иметь дело со строками, чтобы найти приемники!), но, как упоминалось в Paranaix, в GUI это не может быть большой проблемой (хотя в этом случае это может стать проблемой на старых компьютерах, Если вы отправляете все эти сигналы при перемещении sliderbar.)


оба подхода используют соединения сигнал-слот. В первом случае connect вызов производится QMetaObject::connectSlotsByName() позвонили из setupUi. Во втором случае вы явно вызываете connect себя.

кроме того, первый подход не нужен в Qt5 при использовании C++11. Вы можете изменить значение в лямбде:

QObject::connect(ui->slider, &QAbstractSlider::valueChanged,
                 [this](int val){ ui->widget->setValue(val*2); });

для защиты от удаления ui->widget, вы должны использовать QPointer:

class MyWindow : public QMainWindow {
  QPointer<QAbstractSlider> m_widget;
  ...
public:
  MyWindow(QWidget * parent = 0) : QMainWindow(parent) {
    ...
    setupUi(this);
    m_widget = ui->widget;
    QObject::connect(ui->slider, &QAbstractSlider::valueChanged, 
                    [this](int val)
    {
      if (!m_widget.isNull()) m_widget->setValue(val*2); 
    });

накладные расходы соединений сигнал-слот является quantified в этот ответ.


сигнал/слот преимущества:

  • несколько слотов могут быть подключены к одному сигналу, и вы не беспокоитесь о выделении и освобождении памяти для этого
  • вы можете обрабатывать многопоточность с этим

сигнал/слот недостатки:

  • немного медленнее, чем прямой вызов
  • значительно медленнее, если слот виртуальные
  • QObject довольно тяжелая вещь, поэтому вы обычно пытаетесь избежать построения миллиардов их!--4-->

Подробнее здесь


Я предпочитаю второй метод, так как случилось, что я забыл удалить "auto-connect-slots", когда элемент UI был удален, вызывая мертвый код. AFAIK это то же самое" за кулисами " (посмотрите на автоматически сгенерированные qt-файлы).

когда вы хотите изменить значение, я бы предпочел следующий метод:

connect(ui->slider, SIGNAL(valueChanged(int)), this, SLOT(myOwnSlot(int)));

void MainWindow::myOwnSlot(int value) {
    /** do stuff */
    ui->widget->setValue(value);
}

Greetz


сигналы и слоты-это другой стиль кодирования. Вы можете делать с сигналами, которые могут быть полезны и аккуратные способы для традиционного c++. Например, вы можете испускать сигналы const из функций const и подключать их к слотам non const (где, как и в c++, вы не можете совершать вызовы non const из функции const). Мне никогда не нравилось использовать изменяемые объекты, поэтому сигналы обеспечивают мне чистую работу.