Qt moc с реализациями внутри файлов заголовков?

StackOverflow https://stackoverflow.com/questions/3001615

  •  25-09-2019
  •  | 
  •  

Вопрос

Можно ли сообщить Qt MOC, что я хотел бы объявить класс и реализовать его в одном файле, а не разбивать их на файлы .h и .cpp?

Это было полезно?

Решение 4

Я считаю, что это лучше. Это на самом деле, как я строю все мои объекты сейчас.

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();
}

Окно

#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

Построить ... qmake works.pro

сделать

Другие советы

Если вы хотите объявить и внедрить подкласс Qobject в файле CPP, вы должны вручную включить файл MOC.

Например: (файл main.cpp)

struct SubObject : QObject
{
    Q_OBJECT
};

//...

#include "main.moc"

Вы должны Rerun Moc (make qmake) после добавления #include утверждение.

ТЛ;ДР

Да, если вы говорите только о файлах, которые пишете сами (а не о тех, которые генерируются moc).Вам вообще не нужно делать ничего особенного.

Если вы когда-нибудь захотите явно включить вывод moc в файлы, которые вы пишете, есть один случай, когда вы должен сделай это, и один случай, когда ты мощь желаю это сделать.Давайте предположим, что MyObject класс объявлен в MyObject.h и ваше определение этого дано в MyObject.cpp:

  1. MyObject.moc должно быть включено в конце из MyObject.cpp если только ты заявляешь какие-либо Q_OBJECT занятия внутри MyObject.cpp.

  2. 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.

Какое место в этой картине занимает 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 для генерации до двух выходных данных:

  1. moc_foo.cpp от foo.h, если только foo.h содержит Q_OBJECT макрос.

  2. 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.mocmain.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"
...

Я думаю, что вы обычно можете объявить и внедрить класс в заголовом файле без использования ничего особенного, например:

#include <QObject>

class MyClass : public QObject
{
  Q_OBJECT

  public:
     MyClass(QObject * parent)
     {
        // Constructor Content
     }

     methodExample()
     {
          // Method content
     }
};

После этого вы добавляете файл заголовка в файл PRI и выполните qmake еще раз, и это. У вас есть класс, наследующий от Qobject и реализован и объявлен INT HE .h Файл.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top