Использование сигналов 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). Мне никогда не нравилось использовать изменяемые объекты, поэтому сигналы обеспечивают мне чистую работу.