Qt moc с реализациями внутри заголовочных файлов?
можно ли сказать Qt MOC, что я хотел бы объявить класс и реализовать его в одном файле, а не разбивать их на an .h и .cpp файл?
4 ответов
Если вы хотите объявить и реализовать подкласс QObject в файле cpp, вы должны вручную включить файл moc.
например: (файл main.cpp)
struct SubObject : QObject
{
Q_OBJECT
};
//...
#include "main.moc"
вы должны повторить moc (make qmake
) после добавления #include
заявление.
TL; DR
да, если вы говорите только о файлах, которые вы пишете сами (в отличие от тех, которые генерируются moc). Тебе не нужно делать ничего особенного.
если вы когда-нибудь захотите включить вывод moc явно в файлы, которые вы пишете, есть один случай, в котором вы должны сделать это, и один случай, когда вы может желание сделать это. Предположим, что MyObject
класс объявляется в MyObject.h
и ваше определение этого дано в MyObject.cpp
:
MyObject.moc
должно быть входит в конце ofMyObject.cpp
iff вы какие-либоQ_OBJECT
классыMyObject.cpp
.moc_MyObject.cpp
может быть входит в любом месте наMyObject.cpp
чтобы вдвое сократить количество единиц перевода в вашем проекте. Это только оптимизация времени сборки. Если вы этого не сделаете,moc_MyObject.cpp
будет отдельно составленный.
каждый раз, когда вы добавить или удалить Q_OBJECT
макрос из любого источника или файла заголовка, или вы добавляете или удаляете явные включения вывода moc в таких файлах, вы должны повторно запустить qmake/cmake.
чтобы повторно запустить qmake / cmake в Qt Creator, просто щелкните правой кнопкой мыши проект верхнего уровня и выберите запустить qmake или запустить cmake в контекстном меню.
Простой Ответ
An пример проекта Qt на основе qmake может состоять из трех файлов:
# 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
это не делает много, но это, безусловно, действительно. Помимо различных библиотек, с которыми система сборки связывает наш проект, есть два специфичных для проекта единицы перевода: main.cpp
и moc_myobject.cpp
.
хотя кажется, что вся реализация MyObject
находится в заголовочном файле, это действительно не так. The Q_OBJECT
макрос объявляет некоторые биты реализация, которая была бы неопределенной, если бы не созданные moc определения.
как moc входит в картину? Когда проект впервые построен, инструмент metabuild - qmake или cmake-сканирует все входные файлы C++ на наличие Q_OBJECT
макрос. Тем, кто его содержит, уделяется особое внимание. В этом примере проекта, myobject.h
содержит Q_OBJECT
и обрабатывается через moc в moc_myobject.cpp
. Последний добавляется в список источников, которые скомпилирован компилятором C++. Это только концептуально, как если бы вы имели SOURCES += moc_myobject.cpp
на . конечно, вы никогда не должны добавлять такую строку .pro файл.
теперь обратите внимание, что весь реализация MyObject
лежит в двух файлах: myobject.h
и moc_myobject.cpp
. myobject.h
может быть включен в столько единиц перевода, сколько вы хотите - потому что нет внеклассных (автономных) определений, которые нарушали бы правило одного определения. Этот построить систему лечит moc_myobject.cpp
как единый, отдельный блок перевода - это все позаботились для вас.
таким образом, ваша цель достигается без каких-либо усилий: у вас нет ничего особенного, чтобы поставить всю реализацию MyObject
- сохранить для битов, которые MOC производит - в файл заголовка. Это может продлить время компиляции, но в остальном безобидно.
Подход, Нарушающий Правила
это нарушает одно правило определения, точнее, и, таким образом, дает недопустимую программу на C++.
теперь вы можете подумать, чтобы получить "умный" и принудительно включить вывод moc в файл заголовка. Qmake/cmake / qbs будет приспосабливаться и обнаружит это и больше не будет отдельно обрабатывать вывод moc через компилятор, как вы это уже сделали.
Итак, предположим, что в проекте выше вы изменили myobject.h
следующим образом:
// 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
как оно стоит, проект все еще будет компилируйте, по-видимому, выполняя свою цель иметь только один файл, который определяет всю MyObject
- биты, которые вы написали, и биты, которые сгенерировал moc, оба. Но это только из-за маловероятного счастливого обстоятельства: содержание moc_*.cpp
по-прежнему только в одном блоке перевода -
предположим, теперь, когда мы добавляем второй исходный файл в наш проект:
# test.pro
QT += core
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp test.cpp
HEADERS += myobject.h
// test.cpp
#include "myobject.h"
не так много. Это должно сработать, даже если ничего не даст, верно?
увы, это не будет связи. Теперь содержание moc_myobject.cpp
в составе двух единиц перевода. С moc_myobject.cpp
внутренности полны автономных определений членов класса, это нарушает одно правило определения. Правило предписывает, что автономные определения могут отображаться только в одна единица перевод в рамках целевой. Линкер, будучи стражем этого правила, справедливо жалуется.
on включая выход moc в.файл cpp
как указано в TL; DR, ни одно из вышеперечисленных не исключает явного включения вывода moc в источник (.cpp) файлы, в определенных обстоятельствах.
дано " foo.h " и " foo.cpp" и проект, управляемый qmake или cmake, система сборки будет направлять moc
для генерации до двух выходов:
moc_foo.cpp
Сfoo.h
, ifffoo.h
содержитQ_OBJECT
макрос.foo.moc
Сfoo.cpp
, ifffoo.cpp
содержит#include "foo.moc"
.
давайте рассмотрим подробно, почему вы хотите включить любой из них в a .файл cpp.
в том числе ХХХ.МПЦ
иногда, особенно в дни, предшествующие C++11 и Qt 5, удобно объявлять небольшие вспомогательные классы QObject для локального использования только в одной единице перевода (исходный файл).
это также удобно при написании однофайловых, автономных тестовых случаев и примеров для stackoverflow использовать.
Предположим, вы хотите, чтобы кто-то продемонстрировал в одном файле, как вызвать слот из цикла событий:
// 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"
с MyObject
- это небольшой класс, который используется только локально в main.moc
, не имеет смысла помещать его определение в отдельный файл заголовка. The #include "main.moc"
линия будет замечена qmake / cmake, и main.cpp
будет подаваться через moc, в результате чего main.moc
. С main.moc
определяет членов MyObject
, он должен быть включен где-то, где MyObject
объявлена. Поскольку декларация находится в пределах main.cpp
, вы не можете иметь main.moc
быть отдельной единицей перевода: она не будет компилироваться из-за MyObject
не заявил. Единственное место, где он объявлен внутри main.cpp
где-то ближе к концу. Вот почему это безопасная ставка, чтобы всегда включать foo.moc
в конце foo.cpp
.
проницательный читатель теперь спрашивает:Как же так?--38--> получает объявления классов, члены которых он определяет? Довольно просто: это явно включает файл заголовка, из которого он генерируется (здесь:foo.h
). Конечно!--42--> не могу этого сделать, так как это нарушит правило единого определения, умножив определение всего в foo.cpp
.
в том числе moc_xxx.cpp
в особенно больших проектах Qt у вас может быть - в среднем-два файла и две единицы перевода на каждый класс:
-
MyObject.h
иMyObject.cpp
это файлы, которые вы пишете. -
MyObject.cpp
иmoc_MyObject.cpp
являются единицами перевода.
можно вдвое сократить количество единиц перевода, явно включив moc_MyObject.cpp
где-то в MyObject.cpp
:
// MyObject.cpp
#include "MyObject.h"
#include "moc_MyObject.cpp"
...
Я думаю, что вы обычно можете объявить и реализовать класс в файле заголовка, не используя ничего особенного, например:
#include <QObject>
class MyClass : public QObject
{
Q_OBJECT
public:
MyClass(QObject * parent)
{
// Constructor Content
}
methodExample()
{
// Method content
}
};
после этого вы добавляете файл заголовка в файл pri и снова выполняете qmake, и все. У вас есть класс, который наследуется от qobject и реализуется и объявляется int he .H-файл.
Я считаю, что это лучший способ. Именно так я теперь строю все свои объекты.
Qt 4.8.7
работает.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();
}