2015-12-12 35 views
7

Da principiante a C++ qui. Stavo leggendo A Deeper Look at Signals and Slots, che afferma che 1) i callback sono intrinsecamente di tipo non sicuri e 2) per renderli sicuri è necessario definire un wrapper di classe virtuale puro attorno alla propria funzione. Ho difficoltà a capire perché è vero. A titolo di esempio, ecco il codice Qt fornisce sul proprio tutorial page for signals and slots:Perché i segnali e gli slot sono migliori di semplici vecchi callback?

// 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 

qui è che il codice riscritto utilizzando le richiamate:

#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); 

Come si può vedere, la versione di callback è di tipo-sicuro e più breve rispetto alla versione Qt, despite them claiming that it's not. Non definisce nuove classi, a parte lo Counter. Utilizza solo il codice della libreria standard e non ha bisogno di un compilatore speciale (moc) per funzionare. Perché, quindi, i segnali e le bande sono preferiti rispetto ai callback? Il C++ 11 ha semplicemente reso obsoleti questi concetti?

Grazie.

+3

Qt è molto più vecchio di C++ 11. Le affermazioni contenute nella documentazione, nei white paper, ecc. Devono essere lette come affermazioni per "C++ come C migliore rispetto allo standard C++ 11". In particolare affermazioni sciocche come "i richiami hanno due difetti fondamentali: in primo luogo, non sono sicuri per il tipo". non ha senso per C++ come C++: per capirlo bisogna pensare in termini di pura programmazione in C. –

+2

Non confondere "l'implementazione di Qt di segnali e slot" con "segnali e slot" il concetto. Quando qualcosa mantiene come lista di callback e li chiama tutti quando si verifica un evento. hai l'essenza di segnali e slot. Tutto il resto è zucchero, sintassi e marketing. Quello che hai è una multa, seppur semplicistica, implementazione di un sistema di segnale e di slot. – GManNickG

+0

@GManNickG Allora qual è esattamente la differenza tra la mia implementazione e Qt? –

risposta

4

Perché i segnali e gli slot sono migliori di semplici vecchi callback?

Poiché i segnali sono un po 'come i vecchi callback pianura, sulla parte superiore di avere funzioni extra e di essere profondamente integrato con le API Qt. Non è scienza missilistica - callback + funzioni extra + integrazione profonda è maggiore dei callback da solo. C++ potrebbe finalmente offrire un modo più semplice per fare callback, ma ciò non sostituisce i segnali e gli slot Qt, tanto meno renderli obsoleti.

L'aspetto slot è diventato un po 'meno rilevante dal momento che Qt 5, che consentiva di collegare i segnali a qualsiasi funzione. Tuttavia, le slot si integrano con il sistema meta Qt, che viene utilizzato da molte API Qt per far funzionare le cose.

Sì, è possibile utilizzare i callback per quasi tutto ciò che i segnali dovrebbero raggiungere. Ma non è più semplice, è un po 'più prolisso, non gestisce automaticamente le connessioni in coda, non si integrerà con Qt come fanno i segnali, probabilmente si potrebbe aggirare anche questo, ma diventerà ancora più prolisso .

E nel caso di QML, che al giorno d'oggi è l'obiettivo principale di Qt, sei essenzialmente bloccato con i segnali di Qt. Quindi presumo che i segnali siano qui per restare.

I segnali e gli slot sono "migliori" perché Qt è concettualmente costruito intorno a loro, fanno parte dell'API e sono utilizzati da molte API. Quei concetti sono stati in Qt per molto tempo, dai tempi in cui C++ non offriva molto supporto di callback oltre ai normali puntatori di funzione ereditati da C. Questo è anche il motivo per cui Qt non può semplicemente passare a std callback - sarà rompi molte cose ed è uno sforzo inutile. La stessa ragione per cui Qt continua a usare quei maliziosi semplici puntatori non sicuri invece di puntatori intelligenti. Segnali e slot non sono obsoleti come concetto, tanto meno tecnicamente quando si usa Qt. Il C++ è semplicemente arrivato troppo tardi nel gioco. Non è realistico aspettarsi che tutti si affretti ad abbandonare le proprie implementazioni nelle loro gigantesche basi di codice ora che il C++ fornisce finalmente alternative come parte della libreria standard del linguaggio.

6

C'è un'enorme differenza tra i due: thread.

Le richiamate tradizionali sono sempre chiamate nel contesto del thread chiamante. Non così con i segnali e gli slot - purché il thread stia eseguendo un ciclo di eventi (come lo sarà se si tratta di uno QThread) lo slot può trovarsi in qualsiasi thread.

Certo, puoi fare tutto questo manualmente con un callback - Ho scritto molte app Win32 nel corso degli anni che usano le pompe di messaggi stile Windows che gestiscono i callback attraverso i thread - ma è un sacco di codice boilerplate e non molto divertente da scrivere, mantenere o eseguire il debug.