È 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
.
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
:
MyObject.moc
deve essere incluso alla fine di MyObject.cpp
se si dichiara qualsiasi class MyObject.cpp
in MyObject.cpp
.
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.
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.
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.
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:
moc_foo.cpp
da foo.h
, iff foo.h
contiene la macro Q_OBJECT
.
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.
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
.
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