moc Qt avec des implémentations à l'intérieur des fichiers d'en-tête?
Question
Est-il possible de dire Qt MOC que je voudrais déclarer la classe et la mettre en œuvre dans un seul fichier plutôt que les diviser dans un fichier .h et .cpp?
La solution 4
Je crois que ce soit la meilleure façon. Il est en fait la façon dont je construis tous mes objets maintenant.
Qt 4.8.7
Works.pro:
SOURCES += \
main.cpp
HEADERS += \
Window.h \
MyWidget.h
main.cpp
#include <QtGui>
#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 <QtGui>
#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 <QtGui>
class MyWidget : public QLabel
{
Q_OBJECT
public:
MyWidget(QString text) : QLabel(text)
{
// Whatever
}
};
#include "moc_MyWidget.cpp"
#endif // MYWIDGET_H
Construire ... qmake Works.pro
make
Autres conseils
Si vous voulez déclarer et mettre en œuvre une sous-classe de QObject dans votre fichier cpp, vous devez inclure manuellement le fichier moc.
Par exemple: (fichier main.cpp)
struct SubObject : QObject
{
Q_OBJECT
};
//...
#include "main.moc"
Vous devez exécuter à nouveau moc (make qmake
) après avoir ajouté l'instruction #include
.
TL; DR
Oui, si vous parlez seulement les fichiers que vous écrivez vous-même (par opposition que ceux générés par moc). Vous n'avez pas besoin de faire quoi que ce soit spécial.
Si vous souhaitez toujours inclure la sortie moc explicitement dans les fichiers que vous écrivez, il y a un cas où vous devez le faire, et un cas où vous peut souhait pour le faire. Supposons que la classe MyObject
est déclarée dans MyObject.h
et votre définition de celui-ci est donnée dans MyObject.cpp
:
-
MyObject.moc
doit être inclus à la fin deMyObject.cpp
ssi vous déclarez toutes les classes deQ_OBJECT
dansMyObject.cpp
. -
moc_MyObject.cpp
peut être inclus partoutMyObject.cpp
à réduire de moitié le nombre d'unités de traduction dans votre projet. Il est une optimisation accumulation de temps seulement. Si vous ne le faites pas,moc_MyObject.cpp
sera compilé séparément.
Chaque fois que vous ajoutez ou supprimez macro Q_OBJECT
de tout fichier source ou en-tête, ou vous ajoutez ou supprimez des inclusions explicites de sortie moc dans ces fichiers, vous devez exécuter qmake / CMake.
Pour Réexécutez qmake / CMake dans Qt Creator, simplement un clic droit sur le projet de premier niveau, et sélectionnez Exécuter qmake ou Exécuter CMake dans le menu contextuel.
La réponse
Un exemple de projet Qt base qmake peut consister en trois fichiers, comme suit:
# 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 <QObject>
class MyObject : public QObject {
Q_OBJECT
public:
MyObject() {}
Q_SLOT void aSlot() {}
};
#endif // MYOBJECT_H
Il ne fait pas beaucoup, mais il est certainement valable. Mis à part les différentes bibliothèques qui relie le système de construction de notre projet, il y a deux projets spécifiques une règle définition , pour être exact, et donne ainsi un invalide C ++ programme.
Maintenant, vous pourriez penser à obtenir « intelligent » et inclure de force la sortie moc dans le fichier d'en-tête. Les qmake / cmake / QBS seront de logement, et le détectera et ne seront pas traiter séparément sortie moc par le compilateur touteplus, comme vous l'avez déjà fait.
Alors, supposons que, dans le projet ci-dessus, vous avez changé myobject.h
comme suit:
// myobject.h
#ifndef MYOBJECT_H
#define MYOBJECT_H
#include <QObject>
class MyObject : public QObject {
Q_OBJECT
public:
MyObject() {}
Q_SLOT void aSlot() {}
};
#include "moc_myobject.cpp"
#endif // MYOBJECT_H
En l'état actuel, le projet sera toujours compiler, apparemment remplir votre objectif d'avoir un seul fichier qui définit l'ensemble des MyObject
- les bits que vous avez écrit, et les bits qui Moc générés, les deux. Mais il est seulement en raison d'une heureuse circonstance improbable: le contenu de moc_*.cpp
sont toujours en une seule unité de traduction -
Supposons maintenant que l'on ajoute un second fichier source à notre projet:
# test.pro
QT += core
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp test.cpp
HEADERS += myobject.h
// test.cpp
#include "myobject.h"
Pas grand-chose à elle. Il devrait fonctionner, même si elle ne fait pas grand-chose, non?
Hélas, il ne sera pas un lien. Maintenant, le contenu de moc_myobject.cpp
font partie de deux unités de traduction. Étant donné que les faces intérieures de moc_myobject.cpp
sont pleins de membres de classe autonomes définitions, cela porte atteinte à la une règle définition . une unité de traduction Les mandats des règles que les définitions autonomes uniquement peuvent apparaître dans une cible. L'éditeur de liens, être le gardien de cette règle, se plaint à juste titre.
Le Y compris sortie moc dans le fichier .cpp
Comme mentionné dans le TL; DR., Aucun des ci-dessus exclut l'inclusion explicite de la production moc dans les fichiers source (Cpp), dans des circonstances particulières
Etant donné « foo.h » et « foo.cpp », et un projet géré par qmake ou CMake, le système de construction dirigera moc
générer jusqu'à deux sorties:
-
moc_foo.cpp
defoo.h
, ssifoo.h
contient macroQ_OBJECT
. -
foo.moc
defoo.cpp
, ifffoo.cpp
contient#include "foo.moc"
.
Examinons en détail pourquoi voudriez-vous d'inclure un ou l'autre dans un fichier .cpp.
Y compris xxx.moc
Parfois, en particulier dans les jours avant 11 C ++ et Qt 5, il est très utile de déclarer les petites classes de QObject d'aide pour l'utilisation locale dans une unité de traduction (fichier source) uniquement.
est également à portée de main lors de l'écriture unique fichier, cas de test autonomes et des exemples pour stackoverflow.
Supposons que vous vouliez quelqu'un de démontrer, dans un fichier, comment appeler une fente de la boucle d'événement:
// main.cpp
#include <QCoreApplication>
#include <QTextStream>
#include <cstdio>
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"
Depuis MyObject
est une petite classe qui est utilisé localement dans main.moc
, il ne serait pas beaucoup de sens de mettre sa définition dans un fichier d'en-tête séparé. La ligne de #include "main.moc"
sera remarqué par qmake / CMake et main.cpp
sera alimenté par moc, ce qui main.moc
. Depuis main.moc
définit les membres de MyObject
, il doit être inclus quelque part où MyObject
est déclarée. Comme la déclaration est dans main.cpp
, vous ne pouvez pas avoir main.moc
une unité de traduction séparée: il ne compilera pas en raison de MyObject
étant non déclarée. Le seul endroit où elle est déclarée est dans main.cpp
, quelque part vers la fin. Voilà pourquoi il est un pari sûr de toujours inclure foo.moc
à la fin de foo.cpp
.
Un lecteur astucieux demande maintenant: comment se moc_foo.cpp
obtient les déclarations des classes dont les membres définit? Tout simplement: il inclut explicitement le fichier d'en-tête, il est généré à partir (ici: foo.h
). Bien sûr foo.moc
ne peut pas le faire, car il brisaient la règle de définition unique en définissant multiplier tout foo.cpp
.
Y compris moc_xxx.cpp
En particulier les grands projets Qt, vous pourriez avoir - en moyenne - deux fichiers et deux unités de traduction par chaque classe:
-
MyObject.h
etMyObject.cpp
sont les fichiers que vous écrivez. -
MyObject.cpp
etmoc_MyObject.cpp
sont les unités de traduction.
Il est possible de réduire de moitié le nombre d'unités de traduction en incluant explicitement moc_MyObject.cpp
quelque part dans MyObject.cpp
:
// MyObject.cpp
#include "MyObject.h"
#include "moc_MyObject.cpp"
...
Je pense que vous pouvez normalement déclarer et mettre en œuvre la classe dans le fichier d'en-tête sans utiliser quelque chose de spécial, par exemple:
#include <QObject>
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass(QObject * parent)
{
// Constructor Content
}
methodExample()
{
// Method content
}
};
Après cela, vous ajoutez le fichier d'en-tête du fichier pri et d'exécuter qmake à nouveau et c'est tout. Vous avez une classe qui hérite de QObject et est mis en œuvre et a déclaré int il .h.