Unterschiede zwischen stringbasierten und funktorbasierten Verbindungen
Qt bietet zwei verschiedene Möglichkeiten, Signal-Slot-Verbindungen in C++ zu schreiben: die stringbasierte Verbindungssyntax und die Funktor-basierte Verbindungssyntax. Beide Syntaxen haben Vor- und Nachteile. Die folgende Tabelle fasst ihre Unterschiede zusammen.
| Merkmal | String-basiert | Funktor-basiert |
|---|---|---|
| Typüberprüfung | Erfolgt zur Laufzeit | Erfolgt zur Kompilierungszeit |
| Kann implizite Typkonvertierungen durchführen. | Nein | Ja |
| Signale können mit Lambda-Ausdrücken verknüpft werden. | Nein | Ja |
| Signale können mit Slots verbunden werden, die mehr Argumente als das Signal haben (unter Verwendung von Standardparametern). | Ja | Nein |
| C++-Funktionen können mit QML-Funktionen verbunden werden. | Ja | Nein |
In den folgenden Abschnitten werden diese Unterschiede ausführlich erläutert und es wird gezeigt, wie die für die jeweilige Verbindungssyntax spezifischen Funktionen genutzt werden können.
Typüberprüfung und implizite Typkonvertierungen
Bei stringbasierten Verbindungen erfolgt die Typüberprüfung durch den Vergleich von Strings zur Laufzeit.
Es gelten folgende Einschränkungen:
| Einschränkung | Grund |
|---|---|
| Verbindungsfehler können erst erkannt werden, nachdem das Programm gestartet wurde. | Nicht zutreffend |
| Implizite Typumwandlungen zwischen Signalen und Slots sind nicht möglich. | Der String-Komparator hat keinen Zugriff auf C++-Typinformationen und stützt sich daher auf den exakten String-Abgleich. |
| Typedefs und Namespaces können nicht aufgelöst werden. |
Im Gegensatz dazu werden funktorbasierte Verknüpfungen vom Compiler geprüft. Der Compiler erkennt Fehler bereits zur Kompilierungszeit, ermöglicht implizite Typumwandlungen zwischen kompatiblen Typen und erkennt unterschiedliche Namen desselben Typs.
Beispielsweise kann nur die funktorbasierte Syntax verwendet werden, um ein Signal, das einen ` int ` überträgt, mit einem Slot zu verbinden, der einen ` double` akzeptiert. Ein ` QSlider ` enthält einen ` int `-Wert, während ein ` QDoubleSpinBox ` einen ` double `-Wert enthält. Der folgende Codeausschnitt zeigt, wie man sie synchron hält:
auto slider = new QSlider(this);
auto doubleSpinBox = new QDoubleSpinBox(this);
// OK: The compiler can convert an int into a double
connect(slider, &QSlider::valueChanged,
doubleSpinBox, &QDoubleSpinBox::setValue);
// ERROR: The string table doesn't contain conversion information
connect(slider, SIGNAL(valueChanged(int)),
doubleSpinBox, SLOT(setValue(double)));Das folgende Beispiel veranschaulicht das Fehlen einer Namensauflösung. `QAudioInput::stateChanged()` wird mit einem Argument vom Typ „QAudio::State“ deklariert. Daher müssen stringbasierte Verbindungen ebenfalls „QAudio::State“ angeben, selbst wenn ` "State" ` bereits sichtbar ist. Dieses Problem betrifft keine funktorbasierten Verbindungen, da Argumenttypen nicht Teil der Verbindung sind.
auto audioInput = new QAudioInput(QAudioFormat(), this);
auto widget = new QWidget(this);
// OK
connect(audioInput, SIGNAL(stateChanged(QAudio::State)),
widget, SLOT(show()));
// ERROR: The strings "State" and "QAudio::State" don't match
using namespace QAudio;
connect(audioInput, SIGNAL(stateChanged(State)),
widget, SLOT(show()));
// ...Verbindungen zu Lambda-Ausdrücken herstellen
Die Syntax für funktorbasierte Verbindungen ermöglicht es, Signale mit Lambda-Ausdrücken zu verbinden, bei denen es sich im Grunde um Inline-Slots handelt. Diese Funktion ist bei der stringbasierten Syntax nicht verfügbar.
Im folgenden Beispiel sendet die Klasse „TextSender“ ein Signal „ textCompleted() “ aus, das einen Parameter „ QString “ enthält. Hier ist die Klassendeklaration:
class TextSender : public QWidget {
Q_OBJECT
QLineEdit *lineEdit;
QPushButton *button;
signals:
void textCompleted(const QString& text) const;
public:
TextSender(QWidget *parent = nullptr);
};Hier ist die Verbindung, die „ TextSender::textCompleted() “ ausgibt, wenn der Benutzer auf die Schaltfläche klickt:
TextSender::TextSender(QWidget *parent) : QWidget(parent) {
lineEdit = new QLineEdit(this);
button = new QPushButton("Send", this);
connect(button, &QPushButton::clicked, [=] {
emit textCompleted(lineEdit->text());
});
// ...
}In diesem Beispiel hat die Lambda-Funktion die Verbindung vereinfacht, obwohl „ QPushButton::clicked()“ und „ TextSender::textCompleted() “ inkompatible Parameter haben. Im Gegensatz dazu würde eine stringbasierte Implementierung zusätzlichen Boilerplate-Code erfordern.
Hinweis: Die funktorbasierte Verbindungssyntax akzeptiert Zeiger auf alle Funktionen, einschließlich eigenständiger Funktionen und regulärer Mitgliedsfunktionen. Aus Gründen der Lesbarkeit sollten Signale jedoch nur mit Slots, Lambda-Ausdrücken und anderen Signalen verbunden werden.
Verbinden von C++-Objekten mit QML-Objekten
Die stringbasierte Syntax kann C++-Objekte mit QML-Objekten verbinden, die funktorbasierte Syntax hingegen nicht. Dies liegt daran, dass QML-Typen zur Laufzeit aufgelöst werden und dem C++-Compiler daher nicht zur Verfügung stehen.
Im folgenden Beispiel bewirkt ein Klick auf das QML-Objekt, dass das C++-Objekt eine Meldung ausgibt, und umgekehrt. Hier ist der QML-Typ (in „ QmlGui.qml “):
Rectangle {
width: 100; height: 100
signal qmlSignal(string sentMsg)
function qmlSlot(receivedMsg) {
console.log("QML received: " + receivedMsg)
}
MouseArea {
anchors.fill: parent
onClicked: qmlSignal("Hello from QML!")
}
}Hier ist die C++-Klasse:
class CppGui : public QWidget {
Q_OBJECT
QPushButton*button;
signals:
void cppSignal(const QVariant& sentMsg) const;
public slots:
void cppSlot(const QString& receivedMsg) const {
qDebug() << "C++ received:" << receivedMsg;
}
public:
CppGui(QWidget*parent = nullptr) : QWidget(parent) {
button = new QPushButton("Click Me!", this);
connect(button, &QPushButton::clicked, [=] {
emit cppSignal("Hallo aus C++!");
});
}
};Hier ist der Code, der die Signal-Slot-Verbindungen herstellt:
auto cppObj = new CppGui(this);
auto quickWidget = new QQuickWidget(QUrl("QmlGui.qml"), this);
auto qmlObj = quickWidget->rootObject();
// Connect QML signal to C++ slot
connect(qmlObj, SIGNAL(qmlSignal(QString)),
cppObj, SLOT(cppSlot(QString)));
// Connect C++ signal to QML slot
connect(cppObj, SIGNAL(cppSignal(QVariant)),
qmlObj, SLOT(qmlSlot(QVariant)));Hinweis: Alle JavaScript-Funktionen in QML nehmen Parameter vom Typ „ var “ entgegen, der dem Typ „ QVariant “ in C++ entspricht, sofern keine Typangaben verwendet werden. Weitere Details finden Sie unter Aufrufen von QML-Methoden.
Wenn auf das „ QPushButton “ geklickt wird, gibt die Konsole „QML received: ‚Hello from C++!‘“ aus. Ebenso gibt die Konsole beim Klicken auf das „Rectangle“ „C++ received: ‚Hello from QML!‘“ aus.
Weitere Möglichkeiten, wie C++-Objekte mit QML-Objekten interagieren können, finden Sie unter „Interaktion mit QML-Objekten aus C++ “.
Verwendung von Standardparametern in Slots, um eine Verbindung zu Signalen mit weniger Parametern herzustellen
In der Regel kann eine Verbindung nur hergestellt werden, wenn der Slot dieselbe Anzahl an Argumenten wie das Signal (oder weniger) hat und wenn alle Argumenttypen kompatibel sind.
Die stringbasierte Verbindungssyntax bietet eine Umgehungsmöglichkeit für diese Regel: Verfügt der Slot über Standardparameter, können diese Parameter im Signal weggelassen werden. Wird das Signal mit weniger Argumenten als der Slot ausgegeben, führt Qt den Slot mit den Standardparameterwerten aus.
Funktor-basierte Verbindungen unterstützen diese Funktion nicht.
Angenommen, es gibt eine Klasse namens „ DemoWidget “ mit einem Slot „ printNumber() “, der ein Standardargument besitzt:
public slots:
void printNumber(int number = 42) {
qDebug() << "Lucky number" << number;
}Bei Verwendung einer stringbasierten Verbindung kann ` DemoWidget::printNumber() ` mit ` QApplication::aboutToQuit()` verknüpft werden, obwohl letzteres keine Argumente hat. Die funktorbasierte Verbindung führt zu einem Kompilierungsfehler:
DemoWidget::DemoWidget(QWidget*parent) : QWidget(parent) {
// OK: printNumber() wird mit dem Standardwert 42 aufgerufen
connect(qApp, SIGNAL(aboutToQuit()),
this, SLOT(printNumber()));
// FEHLER: Der Compiler verlangt kompatible Argumente
connect(qApp, &QCoreApplication::aboutToQuit,
this, &DemoWidget::printNumber);
}Um diese Einschränkung bei der funktorbasierten Syntax zu umgehen, verbinden Sie das Signal mit einer Lambda-Funktion, die den Slot aufruft. Siehe den obigen Abschnitt „Verbindungen zu Lambda-Ausdrücken herstellen“.
Auswahl überladener Signale und Slots
Bei der stringbasierten Syntax werden die Parametertypen explizit angegeben. Dadurch ist die gewünschte Instanz eines überladenen Signals oder Slots eindeutig.
Im Gegensatz dazu müssen Sie bei der funktorbasierten Syntax explizit angeben, welche Version eines überladenen Signals oder Slots Sie verbinden möchten.
Beispielsweise verfügt „ QLCDNumber “ über drei Versionen des Slots „ display() “:
QLCDNumber::display(int)QLCDNumber::display(double)QLCDNumber::display(QString)
Um die Version „ int “ mit „ QSlider::valueChanged()“ zu verbinden, gibt es zwei Syntaxvarianten:
auto slider = new QSlider(this);
auto lcd = new QLCDNumber(this);
// String-based syntax
connect(slider, SIGNAL(valueChanged(int)),
lcd, SLOT(display(int)));
// Functor-based syntax
connect(slider, &QSlider::valueChanged,
lcd, qOverload<int>(&QLCDNumber::display));Umfassende Beispiele für die Anbindung an überladene Signale und Slots mithilfe von ` qOverload()`, `static_cast` und Lambda-Ausdrücken finden Sie unter „Anbindung an überladene Signale und Slots“.
Siehe auch „ qOverload()“.
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.