前の構文
これは通常、信号とスロットを接続する方法です。
connect(sender, SIGNAL(valueChanged(QString,QString)), receiver, SLOT(updateValue(QString)) );
実際、
SIGNAL
および
SLOT
マクロは引数を文字列に変換します。 次に、
QObject::connect()
は、これらの行を
moc
ユーティリティによってコンパイルされたイントロスペクションデータと比較します。
この構文の問題は何ですか?
一般にすべてがうまく機能するという事実にもかかわらず、まだいくつかの不便さがあります:
- コンパイル時にチェックできない:すべてのチェックは、行の解析後、実行時に実行されます。 そしてこれは、タイプミスがシグナルまたはスロットの名前に忍び込んだ場合、プログラムは正常にコンパイルされますが、接続は作成されないことを意味します。 表示されるのは、実行時の警告のみです。
- すべての操作は文字列を使用して実行されるため、スロット内の型名は信号内の型名と文字通り一致する必要があります。 さらに、ヘッダーファイルと接続を記述するコードで一致する必要があります。 これは、typedefまたは名前空間を使用しようとするときに問題が発生することを意味します。
新しい構文:関数ポインターの使用
今後のQt5は代替構文をサポートします。 上記のアプローチに加えて、信号とスロットを接続するこの新しい方法を使用できます。
connect(sender, &Sender::valueChanged, receiver, &Receiver::updateValue );
どちらが美しいかは好みの問題です。 しかし、新しいオプションに慣れるのは非常に簡単です。
次に、それがもたらす利点を検討します。
コンパイル時に確認する
シグナルまたはスロットの名前を間違えた場合、またはスロットの引数がシグナルの引数と一致しない場合、コンパイルエラーが発生します。 これにより、リファクタリング後の時間を節約できます。
さらに、
static_assert
、引数が一致しない場合や
Q_OBJECT
スキップされる場合に明確なエラーを表示するため
static_assert
使用されました。
引数型の自動キャスト
これで、typedefまたは名前空間の使用を恐れることなく、暗黙的なキャストが可能な場合、他の型の引数を受け入れるスロットに信号を接続できます。
次の例では、パラメーターとして
QString
を受け入れる信号を
QVariant
を受け入れるスロットに接続します。
QVariant
は
QString
を受け入れる暗黙的なコンストラクタがあるため、これは問題なく機能します。
class Test : public QObject { Q_OBJECT public: Test() { connect(this, &Test::someSignal, this, &Test::someSlot); } signals: void someSignal(const QString &); public: void someSlot(const QVariant &); };
信号を任意の機能に接続します
前の例で
someSlot
たように、
someSlot
は、
slot
なしの単純なパブリックメソッドとして宣言されました。 Qtはスロットを直接呼び出すことができ、このためにイントロスペクションを必要としなくなりました。 (信号にはまだ必要ですが)
しかし今では、sinalを任意の関数またはファンクターに接続することもできます。
static void someFunction() { qDebug() << "pressed"; } // ... somewhere else QObject::connect(button, &QPushButton::clicked, someFunction);
これは、boostまたはtr1 :: bindと組み合わせて非常に強力な機能になることができます。
C ++ 11の無名関数
以前に説明されたすべては、古いC ++ 98で動作します。 ただし、C ++ 11をサポートするコンパイラを使用する場合は、新しい言語機能を使用することを強くお勧めします。 ラムダ式は、少なくともMSVC 2010、GCC 4.5、clang 3.1でサポートされています。 最後の2つについては、フラグとして
-std=c++0x
を指定する必要があります。
これで、次のコードを記述できます。
void MyWindow::saveDocumentAs() { QFileDialog *dlg = new QFileDialog(); dlg->open(); QObject::connect(dlg, &QDialog::finished, [=](int result) { if (result) { QFile file(dlg->selectedFiles().first()); // ... save document here ... } dlg->deleteLater(); }); }
これにより、非同期コードの作成が非常に簡単になります。