2015-12-12 9 views
7

初心者〜C++はこちら。私はA Deeper Look at Signals and Slotsを読んでいました.1)コールバックは本質的に型が安全ではないと主張しています.2)安全にするために、関数の周りに純粋な仮想クラスラッパーを定義する必要があります。私はなぜそれが本当であるか理解するのは難しいです。例として、ここでのコードQtは自分tutorial page for signals and slotsに提供されています。ここではシグナルとスロットは単純な古いコールバックよりも優れているのはなぜですか?

// Header file 
#include <QObject> 

class Counter : public QObject 
{ 
    Q_OBJECT 

public: 
    Counter() { m_value = 0; } 

    int value() const { return m_value; } 

public slots: 
    void setValue(int value); 

signals: 
    void valueChanged(int newValue); 

private: 
    int m_value; 
}; 

// .cpp file 
void Counter::setValue(int value) 
{ 
    if (value != m_value) { 
     m_value = value; 
     emit valueChanged(value); 
    } 
} 

// Later on... 
Counter a, b; 
QObject::connect(&a, SIGNAL(valueChanged(int)), 
       &b, SLOT(setValue(int))); 

a.setValue(12);  // a.value() == 12, b.value() == 12 
b.setValue(48);  // a.value() == 12, b.value() == 48 

は、コールバックを使用して書き直すことコードです:

#include <functional> 
#include <vector> 

class Counter 
{ 
public: 
    Counter() { m_value = 0; } 

    int value() const { return m_value; } 
    std::vector<std::function<void(int)>> valueChanged; 

    void setValue(int value); 

private: 
    int m_value; 
}; 

void Counter::setValue(int value) 
{ 
    if (value != m_value) { 
     m_value = value; 
     for (auto func : valueChanged) { 
      func(value); 
     } 
    } 
} 

// Later on... 
Counter a, b; 
auto lambda = [&](int value) { b.setValue(value); }; 
a.valueChanged.push_back(lambda); 

a.setValue(12); 
b.setValue(48); 

あなたが見ることができるように、コールバックのバージョンがタイプセーフされ、短いですQtバージョンよりも、despite them claiming that it's notです。 Counter以外の新しいクラスは定義しません。標準ライブラリコードのみを使用し、特別なコンパイラ(moc)を使用する必要はありません。なぜ、コールバックよりもシグナルとスロットのほうが好まれますか? C++ 11は単にこれらの概念を廃止しましたか?

ありがとうございました。

+3

QtはC++ 11よりもずっと古いです。 Qtのドキュメントやホワイトペーパーなどのクレームは、C++ 11以前の「C++としてのC++」の主張として読まれなければなりません。特に、「コールバックには2つの根本的な欠陥があります。まず第一に、タイプセーフではありません。 C++にとって意味をなさないのはC++です:これを理解するには、純粋なCプログラミングの観点から考える必要があります。 –

+2

"Qtの信号とスロットの実装"と "信号とスロット"のコンセプトを融合させないでください。コールバックのリストとして何かが維持され、イベントが発生したときにコールバックします。あなたはシグナルとスロットの本質を持っています。それ以外はすべて砂糖、構文、マーケティングです。あなたが持っているのは、信号とスロットシステムの単純化されていれば、うまく実装されていることです。 – GManNickG

+0

@ GManNickG次に、実装とQtの違いは何ですか? –

答えて

4

普通のコールバックよりも信号とスロットが優れているのはなぜですか?

信号は余分な機能を持ち、Qt APIと深く統合されていることに加えて、古いコールバックとよく似ているからです。ロケット科学ではありません - コールバック+追加機能+深い統合は、コールバックだけではありません。 C++は最終的にコールバックを行うためのよりクリーンな方法を提供しているかもしれませんが、Qtシグナルとスロットに取って代わるものではありません。

Qt 5以降、スロットアスペクトの関連性が少し低下しました。これにより、信号がどの機能にも接続できるようになりました。しかし、スロットはQtメタシステムと統合されています。これは、多くのQt APIが動作するために使用されます。

はい、信号が達成されるはずのものすべてにコールバックを使用できます。しかし、それは簡単ではなく、もう少し冗長であり、キューされた接続を自動的に処理しません。信号のやり方によってQtと統合されません。おそらくそれを回避することもできますが、もっと冗長になります。

Qtの主な焦点であるQMLの場合、あなたは本質的にQtの信号に固執しています。だから私は信号がここにあると推測します。

シグナルとスロットは概念的にはそれらの周りに構築されているため、「より良い」ものです。それらはAPIの一部であり、多くのAPIで使用されています。これらのコンセプトは、Ctから長年にわたってQtに入っていました.C++は、Cから継承した単純な古い関数ポインタ以外に、多くのコールバックサポートを提供していませんでした。これはQtがstdコールバックに単純に切り替えることができない理由です。多くのものを壊し、不必要な努力です。同様の理由で、Qtはスマートポインタの代わりに悪い安全でない普通の古いポインタを使い続けています。信号とスロットは、コンセプトとして時代遅れではなく、技術的にもQtを使用する場合はあまりありません。 C++は単にゲームで遅すぎた。 C++が最終的に言語標準ライブラリの一部として選択肢を提供するようになり、誰もが巨大なコードベースで独自の実装から離れようと急ぐことになるのは、現実的ではありません。

6

2つの大きな違いがあります:スレッド。

従来のコールバックは、常にで、呼び出しスレッドのコンテキストで呼び出されます。シグナルとスロットではそうではありません - スレッドがイベントループを実行している限り(それはQThreadのようになります)、スロットはどのスレッドにもあります。

確かに、コールバックを使用してこのすべてを手動で行うことができます。私は、スレッド間でコールバックを混乱させるWindowsスタイルのメッセージポンプを使用するWin32アプリケーションを長年にわたって書いてきましたが、書き込み、保守、デバッグするのが楽しいことではありません。

関連する問題