Qt moc con le implementazioni all’interno dei file di intestazione?

È ansible dire al QOC MOC che vorrei dichiarare la class e implementarla in un singolo file piuttosto che dividerli in un file .h e .cpp?

Se si desidera dichiarare e implementare una sottoclass QObject nel proprio file cpp, è necessario includere manualmente il file moc.

Ad esempio: (file main.cpp)

struct SubObject : QObject { Q_OBJECT }; //... #include "main.moc" 

Devi eseguire nuovamente make qmake ( make qmake ) dopo aver aggiunto l’istruzione #include .

TL; DR

Sì, se stai parlando solo dei file che scrivi tu stesso (al contrario di quelli generati da moc). Non è necessario fare nulla di speciale.

Se si desidera includere esplicitamente l’output di moc nei file che si scrivono, c’è un caso in cui è necessario farlo e un caso in cui si potrebbe desiderare di farlo. Supponiamo che la class MyObject sia dichiarata in MyObject.h e che la tua definizione sia data in MyObject.cpp :

  1. MyObject.moc deve essere incluso alla fine di MyObject.cpp se si dichiara qualsiasi class MyObject.cpp in MyObject.cpp .

  2. moc_MyObject.cpp può essere incluso ovunque in MyObject.cpp per dimezzare il numero di unità di traduzione nel progetto. È solo un’ottimizzazione in fase di costruzione. Se non lo fai, moc_MyObject.cpp verrà compilato separatamente.

Ogni volta che aggiungi o rimuovi la macro Q_OBJECT da qualsiasi file di origine o di intestazione, o aggiungi o rimuovi inclusioni esplicite dell’output di moc in tali file, devi rieseguire qmake / cmake.

Per rieseguire qmake / cmake in Qt Creator, è sufficiente fare clic con il tasto destro del mouse sul progetto di livello superiore e selezionare Esegui qmake o Esegui cmake dal menu di scelta rapida.

La semplice risposta

Un esempio di progetto Qt basato su qmake può consistere di tre file, come segue:

 # test.pro QT += core CONFIG += console CONFIG -= app_bundle TEMPLATE = app SOURCES += main.cpp HEADERS += myobject.h // main.cpp #include "myobject.h" int main() { MyObject obj; obj.staticMetaObject; // refer to a value defined in moc output return 0; } // myobject.h #ifndef MYOBJECT_H #define MYOBJECT_H #include  class MyObject : public QObject { Q_OBJECT public: MyObject() {} Q_SLOT void aSlot() {} }; #endif // MYOBJECT_H 

Non fa molto, ma è certamente valido. Oltre alle varie librerie con cui il sistema di costruzione collega il nostro progetto, ci sono due unità di traduzione specifiche per il progetto: main.cpp e moc_myobject.cpp .

Anche se sembra che l’intera implementazione di MyObject si trovi nel file di intestazione, in realtà non lo è. La macro Q_OBJECT dichiara alcuni bit di implementazione che sarebbero indefiniti, se non fosse per le definizioni generate da moc.

Come entra il moc nella foto? Quando il progetto viene creato per la prima volta, lo strumento metabuild – qmake o cmake – analizza tutti i file di input C ++ per la presenza della macro Q_OBJECT . Quelli che lo contengono, ricevono un trattamento speciale. In questo progetto di esempio, myobject.h contiene Q_OBJECT ed è elaborato tramite moc_myobject.cpp in moc_myobject.cpp . Quest’ultimo viene aggiunto all’elenco delle fonti compilate dal compilatore C ++. È, solo concettualmente, come se avessi SOURCES += moc_myobject.cpp nel file .pro . Ovviamente non si dovrebbe mai aggiungere una riga del genere al file .pro.

Ora si noti che l’ intera implementazione di MyObject basa su due file: myobject.h e moc_myobject.cpp . myobject.h può essere incluso in tutte le unità di traduzione che desideri, perché non ci sono definizioni (standalone) fuori dalla class che violerebbero la regola di una definizione. Il sistema di generazione tratta moc_myobject.cpp come una singola unità di traduzione separata – questo è tutto a posto per te.

In questo modo il tuo objective viene raggiunto senza alcuno sforzo: non hai nulla di speciale da fare per mettere l’intera implementazione di MyObject – salva per i bit che moc produce – nel file di intestazione. Può prolungare i tempi di compilazione, ma è comunque innocuo.

Un approccio violento alle regole

Viola la regola di una definizione , per essere esatti, e quindi produce un programma C ++ non valido.

Ora si potrebbe pensare di diventare “intelligenti” e includere forzatamente l’output Moc nel file di intestazione. Il qmake / cmake / qbs sarà accomodante, e lo scoprirà e non elaborerà separatamente l’output dei moc attraverso il compilatore, come hai già fatto.

Quindi, supponiamo che, nel progetto precedente, tu abbia cambiato myobject.h in questo modo:

 // myobject.h #ifndef MYOBJECT_H #define MYOBJECT_H #include  class MyObject : public QObject { Q_OBJECT public: MyObject() {} Q_SLOT void aSlot() {} }; #include "moc_myobject.cpp" #endif // MYOBJECT_H 

Così com’è, il progetto verrà comunque compilato, apparentemente soddisfacendo il tuo objective di avere un solo file che definisce l’interezza di MyObject – i bit che hai scritto, e i bit generati da moc, entrambi. Ma è solo a causa di circostanze improbabili: il contenuto di moc_*.cpp è ancora in una sola unità di traduzione –

Supponiamo, ora che aggiungiamo un secondo file sorgente al nostro progetto:

 # test.pro QT += core CONFIG += console CONFIG -= app_bundle TEMPLATE = app SOURCES += main.cpp test.cpp HEADERS += myobject.h // test.cpp #include "myobject.h" 

Non c’è molto da fare. Dovrebbe funzionare, anche se non fa molto, giusto?

Ahimè, non collegherà. Ora il contenuto di moc_myobject.cpp fa parte di due unità di traduzione. Poiché le parti moc_myobject.cpp di moc_myobject.cpp sono piene di definizioni dei membri della class autonoma, ciò viola la regola della definizione unica . La regola impone che le definizioni autonome possano apparire solo in una unità di traduzione all’interno di un objective. Il linker, essendo il guardiano di questa regola, si lamenta giustamente.

Incluso Output moc nel file .cpp

Come accennato nel TL; DR, nessuno dei precedenti preclude l’inclusione esplicita dell’output di moc nei file di origine (.cpp), in circostanze specifiche.

Dato “foo.h” e “foo.cpp”, e un progetto gestito da qmake o cmake, il sistema di generazione dirigerà il moc per generare fino a due output:

  1. moc_foo.cpp da foo.h , iff foo.h contiene la macro Q_OBJECT .

  2. foo.moc da foo.cpp , iff foo.cpp contiene #include "foo.moc" .

Esaminiamo in dettaglio perché vorresti includere uno di essi in un file .cpp.

Compreso xxx.moc

A volte, specialmente nei giorni precedenti a C ++ 11 e Qt 5, è utile dichiarare classi di QObject helper di piccole dimensioni solo per uso locale all’interno di un’unità di traduzione (solo file sorgente).

Ciò risulta utile anche quando si scrivono casi di test autonomi e file singoli ed esempi per l’uso di stackoverflow.

Supponiamo che desideri che qualcuno mostri, in un file, come richiamare uno slot dal ciclo degli eventi:

 // main.cpp #include  #include  #include  QTextStream out(stdout); class MyObject : public QObject { Q_OBJECT public: MyObject() {} Q_SLOT void mySlot() { out << "Hello from " << __FUNCTION__ << endl; } }; int main(int argc, char ** argv) { QCoreApplication app(argc, argv); MyObject obj; QMetaObject::invokeMethod(&obj, Qt::QueuedConnection, "mySlot"); QMetaObject::invokeMethod(&app, Qt::QueuedConnection, "quit"); return app.exec(); } #include "main.moc" 

Poiché MyObject è una class piccola che viene utilizzata solo localmente in main.moc , non avrebbe molto senso inserire la sua definizione in un file di intestazione separato. La linea #include "main.moc" verrà notata da qmake / cmake, e main.cpp sarà alimentato tramite moc, risultando in main.moc . Poiché main.moc definisce i membri di MyObject , deve essere incluso in un posto dove viene dichiarato MyObject . Poiché la dichiarazione è all'interno di main.cpp , non è ansible avere main.moc come un'unità di traduzione separata: non verrà compilata poiché MyObject non viene dichiarato. L'unico posto in cui è dichiarato è all'interno di main.cpp , da qualche parte verso la fine. Ecco perché è una scommessa sicura includere sempre foo.moc alla fine di foo.cpp .

Un lettore astuto ora chiede: come moc_foo.cpp ottiene le dichiarazioni delle classi di cui i membri definisce? Molto semplicemente: include esplicitamente il file di intestazione da cui è stato generato (qui: foo.h ). Ovviamente foo.moc non può farlo, dal momento che romperebbe la singola regola di definizione moltiplicando la definizione di tutto in foo.cpp .

Compreso moc_xxx.cpp

In progetti Qt particolarmente grandi, potresti avere - in media - due file e due unità di traduzione per ogni class:

  • MyObject.h e MyObject.cpp sono i file che scrivi.
  • MyObject.cpp e moc_MyObject.cpp sono le unità di traduzione.

È ansible dimezzare il numero di unità di traduzione includendo in modo esplicito moc_MyObject.cpp da qualche parte in MyObject.cpp :

 // MyObject.cpp #include "MyObject.h" #include "moc_MyObject.cpp" ... 

Penso che di solito puoi dichiarare e implementare la class nel file di intestazione senza usare niente di speciale, ad es .:

 #include  class MyClass : public QObject { Q_OBJECT public: MyClass(QObject * parent) { // Constructor Content } methodExample() { // Method content } }; 

Dopo questo, aggiungi il file di intestazione al file pri ed esegui di nuovo qmake e il gioco è fatto. Hai una class che eredita da qobject e viene implementata e dichiarata nel file .hh.

Credo che questo sia il modo migliore. In realtà è come costruisco tutti i miei oggetti ora.

Qt 4.8.7

Works.pro:

 SOURCES += \ main.cpp HEADERS += \ Window.h \ MyWidget.h 

main.cpp

 #include  #include "Window.h" int main(int argc, char *argv[]) { QApplication app(argc,argv); Window window; window.show(); return app.exec(); } 

Window.h

 #ifndef WINDOW_H #define WINDOW_H #include  #include "MyWidget.h" class Window : public QWidget { Q_OBJECT private: MyWidget *whatever; public: Window() { QHBoxLayout *layout = new QHBoxLayout; setLayout(layout); whatever = new MyWidget("Screw You"); layout->addWidget(whatever); } }; #include "moc_Window.cpp" #endif // WINDOW_H 

MyWidget.h

 #ifndef MYWIDGET_H #define MYWIDGET_H #include  class MyWidget : public QLabel { Q_OBJECT public: MyWidget(QString text) : QLabel(text) { // Whatever } }; #include "moc_MyWidget.cpp" #endif // MYWIDGET_H 

Costruisci … qmake Works.pro

rendere