Qt moc与头文件内的实现?

时间:2010-06-08 21:57:38

标签: c++ qt moc

是否有可能告诉Qt MOC我想声明该类并在单个文件中实现它而不是将它们分成.h和.cpp文件?

4 个答案:

答案 0 :(得分:17)

如果要在cpp文件中声明并实现QObject子类,则必须手动包含moc文件。

例如:(文件main.cpp)

struct SubObject : QObject
{
    Q_OBJECT
};

//...

#include "main.moc"

添加make qmake语句后,您必须重新运行moc(#include)。

答案 1 :(得分:11)

TL; DR

是的,如果您只谈论自己编写的文件(而不是moc生成的文件)。你根本不需要做任何特别的事情。

如果您希望在您编写的文件中明确包含moc输出,则有一种情况是必须执行此操作,以及可能希望的一种情况去做吧。我们假设在MyObject中声明了MyObject.h类,并且您在MyObject.cpp中给出了它的定义:

  1. MyObject.moc 必须包含MyObject.cpp 的结尾 iff 您声明任何{{ 1}} Q_OBJECT内的类。

  2. MyObject.cpp 可以包含在<{1}}中,以减少项目中翻译单元的数量。它只是一个构建时优化。如果您不这样做,moc_MyObject.cpp将单独编译。

  3. 每次在任何源文件或头文件中添加或删除MyObject.cpp宏,或者在此类文件中添加或删除moc输出的显式包含时,都必须重新运行qmake / cmake。

    要在Qt Creator中重新运行qmake / cmake,只需右键单击顶层项目,然后从上下文菜单中选择运行qmake 运行cmake 。 / p>

    简单答案

    基于qmake的Qt项目示例可能包含三个文件,如下所示:

    moc_MyObject.cpp

    它没有做太多,但它确实有效。除了构建系统链接我们项目的各种库之外,还有两个特定于项目的translation unitsQ_OBJECT# 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宏声明了一些未定义的实现,如果它不是moc生成的定义。

    moc如何进入图片?首次构建项目时,元构建工具(qmake或cmake)会扫描所有C ++输入文件中是否存在MyObject宏。包含它的那些,给予特殊待遇。在此示例项目中,Q_OBJECT包含Q_OBJECT,并通过moc处理为myobject.h。后者被添加到由C ++编译器编译的源列表中。只是在概念上,就好像Q_OBJECT文件中有moc_myobject.cpp一样。 当然,您永远不应该在.pro文件中添加这样的行。

    现在请注意SOURCES += moc_myobject.cpp整个实施位于两个文件中:.proMyObjectmyobject.h可以包含在您想要的任意数量的翻译单元中 - 因为没有违反一个定义规则的类外(独立)定义。构建系统将moc_myobject.cpp视为单独的单独翻译单元 - 这一切都将由您完成。

    因此,您可以毫不费力地实现目标:将myobject.h的整个实现 - 除了moc产生的位 - 放入头文件中没有什么特别的。它可能会延长编译时间,但无关紧要。

    违反规则的方法

    确切地说,它违反了one definition rule,从而产生了无效的C ++程序。

    现在你可能会想到&#34;聪明&#34;并强制将moc输出包含在头文件中。 qmake / cmake / qbs将适应,并将检测到这一点,并且不再通过编译器单独处理moc输出,因为您已经完成了它。

    因此,假设在上面的项目中,您将moc_myobject.cpp更改为如下所示:

    MyObject

    按照目前的情况,该项目仍将进行编译,似乎实现了您只需要一个文件来定义myobject.h的全部内容的目标 - 您编写的位以及moc生成的位。但这仅仅是由于不太可能的快乐情况:// 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_*.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" 内部充满了独立的成员定义,因此违反了one definition rule。该规则要求独立定义只能出现在目标中的一个翻译单元中。作为这条规则的守护者,联系人正确地抱怨。

    在.cpp文件中包含moc输出

    正如TL; DR中所提到的,在特定情况下,上述任何一项都不排除在源(.cpp)文件中明确包含moc输出。

    鉴于&#34; foo.h&#34;和&#34; foo.cpp&#34;,以及由qmake或cmake管理的项目,构建系统将指示moc_myobject.cpp生成最多两个输出:

      来自moc_myobject.cpp
    1. moc iff moc_foo.cpp包含foo.h宏。

    2. 来自foo.h
    3. Q_OBJECT iff foo.moc包含foo.cpp

    4. 让我们详细研究一下为什么要在.cpp文件中包含任何一个。

      包括xxx.moc

      有时候,特别是在C ++ 11和Qt 5之前的日子里,只能在一个翻译单元(源文件)中声明小辅助QObject类供本地使用。

      在编写单文件,自包含测试用例和stackoverflow使用示例时,这也很方便。

      假设您希望有人在一个文件中演示如何从事件循环中调用插槽:

      foo.cpp

      由于#include "foo.moc"是一个仅在// 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" 中本地使用的小类,因此将其定义放入单独的头文件中是没有意义的。 qmake / cmake会注意到MyObject行,main.moc将通过moc传递,导致#include "main.moc"。由于main.cpp定义了main.moc的成员,因此必须将其包含在声明main.moc的位置。由于声明在MyObject范围内,因此您无法MyObject成为单独的翻译单元:由于main.cpp未被声明,因此无法编译。声明它的唯一地方是main.moc,在某个地方到达终点。这就是为什么在MyObject结束时始终包含main.cpp的安全赌注。

      一位精明的读者现在问:foo.moc如何获得其定义成员的类的声明?很简单:它明确包含从中生成的头文件(此处:foo.cpp)。当然moc_foo.cpp无法做到这一点,因为它通过在foo.h中多次定义所有内容来打破单一定义规则。

      包括moc_xxx.cpp

      在特别大的Qt项目中,平均来说,每个类可能有两个文件两个翻译单元:

      • foo.mocfoo.cpp是您撰写的文件。
      • MyObject.hMyObject.cpp是翻译单元。

      通过在MyObject.cpp中的某处明确包含moc_MyObject.cpp,可以将翻译单元数量减半:

      moc_MyObject.cpp

答案 2 :(得分:3)

我认为您通常可以在头文件中声明和实现该类,而无需使用任何特殊内容,例如:

#include <QObject>

class MyClass : public QObject
{
  Q_OBJECT

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

     methodExample()
     {
          // Method content
     }
};

在此之后,将头文件添加到pri文件并再次执行qmake,就是这样。你有一个继承自qobject的类,并在.h文件中实现和声明。

答案 3 :(得分:-3)

我相信这是最好的方式。这实际上就是我现在如何构建我的所有对象。

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

...构建 qmake Works.pro