TL; DR
Да, если вы говорите только о файлах, которые пишете сами (в отличие от файлов, сгенерированных moc). Вам вообще не нужно делать ничего особенного.
Если вы когда-нибудь захотите явно включить вывод moc в файлы, которые вы пишете, есть один случай, когда вы должны сделать это, и один случай, когда вы можете сделать это. Это. Давайте предположим, что класс MyObject
объявлен в MyObject.h
, а ваше определение его дано в MyObject.cpp
:
MyObject.moc
должно быть включено в конце из MyObject.cpp
тогда и только тогда вы объявляете любые Q_OBJECT
классы в пределах MyObject.cpp
.
moc_MyObject.cpp
может быть включено в любом месте в MyObject.cpp
, чтобы вдвое сократить количество единиц перевода в вашем проекте. Это только оптимизация во время сборки. Если вы этого не сделаете, moc_MyObject.cpp
будет скомпилирован отдельно.
Каждый раз, когда вы добавляете или удаляете макрос Q_OBJECT
из любого исходного файла или файла заголовка, или добавляете или удаляете явные включения вывода moc в такие файлы, вы должны перезапускать qmake / cmake.
Чтобы перезапустить qmake / cmake в Qt Creator, просто щелкните правой кнопкой мыши проект верхнего уровня и выберите Запустить qmake или Запустить cmake из контекстного меню.
Простой ответ
Пример проекта 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
находится в заголовочном файле, на самом деле это не так. Макрос Q_OBJECT
объявляет некоторые биты реализации, которые были бы неопределенными, если бы не определения, сгенерированные moc.
Как Мок входит в картинку? Когда проект создается впервые, инструмент мета-сборки - qmake или cmake - сканирует все входные файлы C ++ на наличие макроса Q_OBJECT
. Тем, которые его содержат, предоставляется особый режим. В этом примере проекта myobject.h
содержит Q_OBJECT
и обрабатывается через moc в moc_myobject.cpp
. Последний добавляется в список источников, которые компилируются компилятором C ++. Это только концептуально, как если бы вы имели SOURCES += moc_myobject.cpp
в файле .pro
. Конечно, вы никогда не должны добавлять такую строку в файл .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
полны автономных определений членов класса, это нарушает правило одного определения . Правило требует, чтобы автономные определения могли появляться только в одной единице перевода в пределах цели. Компоновщик, являясь хранителем этого правила, справедливо жалуется.
Включено Включение вывода moc в файл .cpp
Как упоминалось в TL; DR, ни одно из вышеперечисленного не исключает явного включения вывода moc в исходные (.cpp) файлы при определенных обстоятельствах.
С учетом "foo.h" и "foo.cpp" и проекта, управляемого qmake или cmake, система сборки будет указывать moc
на генерацию до двух выходных данных:
moc_foo.cpp
из foo.h
, в том случае, если foo.h
содержит Q_OBJECT
макрос.
foo.moc
из foo.cpp
, в том случае, если foo.cpp
содержит #include "foo.moc"
.
Давайте подробно рассмотрим, почему вы хотите включить один из них в файл .cpp.
Включая xxx.moc
Иногда, особенно в дни, предшествующие 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
, было бы бессмысленно помещать его определение в отдельный заголовочный файл. Строка #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
.
Проницательный читатель теперь спрашивает: почему moc_foo.cpp
получает объявления классов, чьи члены он определяет? Проще говоря: он явно включает заголовочный файл, из которого он сгенерирован (здесь: foo.h
). Конечно, foo.moc
не может этого сделать, поскольку он нарушил бы правило единого определения, умножив все определения в 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"
...