Qt moc 在头文件中实现?

发布于 2024-09-05 00:43:51 字数 61 浏览 11 评论 0原文

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

Is it possible to tell the Qt MOC that I would like to declare the class and implement it in a single file rather than splitting them up into an .h and .cpp file?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

2024-09-12 00:43:51

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

例如:(文件main.cpp)

struct SubObject : QObject
{
    Q_OBJECT
};

//...

#include "main.moc"

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

If you want to declare and implement a QObject subclass in you cpp file, you have to manually include the moc file.

For example: (file main.cpp)

struct SubObject : QObject
{
    Q_OBJECT
};

//...

#include "main.moc"

You have to rerun moc (make qmake) after adding the #include statement.

梦在夏天 2024-09-12 00:43:51

TL;DR

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

如果您希望在您编写的文件中显式包含 moc 输出,则在一种情况下您必须这样做,在另一种情况下您可能希望这样做。假设 MyObject 类在 MyObject.h 中声明,并且它的定义在 MyObject.cpp 中给出:

  1. MyObject.moc 必须包含在 MyObject.cpp末尾当且仅当您声明的任何 < MyObject.cpp 中的 code>Q_OBJECT 类。

  2. moc_MyObject.cpp 可以包含在MyObject.cpp任何地方,以将翻译单元的数量减半你的项目。这只是构建时优化。如果不这样做,moc_MyObject.cpp将被单独编译。

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

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

简单的答案

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

# 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.cppmoc_myobject.cpp

尽管看起来 MyObject 的整个实现都在头文件中,但实际上并非如此。 Q_OBJECT 宏声明了一些未定义的实现,如果不是 moc 生成的定义的话。

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

现在请注意,MyObject整个实现位于两个文件中:<代码>myobject.h 和moc_myobject.cppmyobject.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 的内部充满了独立的类成员定义,这违反了

关于在 .cpp 文件中包含 moc 输出

正如 TL;DR 中提到的,在特定情况下,上述内容均不排除在源 (.cpp) 文件中显式包含 moc 输出。

给定“foo.h”和“foo.cpp”以及由 qmake 或 cmake 管理的项目,构建系统将指示 moc 生成最多两个输出:

  1. moc_foo。来自 foo.h 的 cppiff foo.h 包含 Q_OBJECT 宏。

  2. foo.moc 来自 foo.cppiff 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.cpp 末尾始终包含 foo.moc 是一个安全的选择。

精明的读者现在会问:moc_foo.cpp 如何获取其定义的成员的类的声明?很简单:它显式包含生成它的头文件(此处:foo.h)。当然,foo.moc 不能这样做,因为它会通过多重定义 foo.cpp 中的所有内容来打破单一定义规则。

包括 moc_xxx.cpp

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

  • MyObject.hMyObject。 cpp 是您编写的文件。
  • MyObject.cppmoc_MyObject.cpp 是翻译单元。

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

// MyObject.cpp
#include "MyObject.h"
#include "moc_MyObject.cpp"
...

TL;DR

Yes, if you're talking only about the files that you write yourself (as opposed as those generated by moc). You don't need to do anything special at all.

If you ever wish to include moc output explicitly in the files you write, there is one case in which you must do it, and one case where you might wish to do it. Let's assume that MyObject class is declared in MyObject.h and your definition of it is given in MyObject.cpp:

  1. MyObject.moc must be included at the end of MyObject.cpp iff you declare any Q_OBJECT classes within MyObject.cpp.

  2. moc_MyObject.cpp can be included anywhere in MyObject.cpp to halve the number of translation units in your project. It's a build-time optimization only. If you don't do it, moc_MyObject.cpp will be separately compiled.

Every time you add or remove Q_OBJECT macro from any source or header file, or you add or remove explicit inclusions of moc output in such files, you must re-run qmake/cmake.

To re-run qmake/cmake in Qt Creator, simply right-click on the toplevel project, and select Run qmake or Run cmake from the context menu.

The Simple Answer

An example qmake-based Qt project might consist of three files, as follows:

# 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

It doesn't do much, but it's certainly valid. Apart from the various libraries that the build system links our project with, there are two project-specific translation units: main.cpp and moc_myobject.cpp.

Even though it appears that the entire implementation of MyObject is in the header file, it really isn't. The Q_OBJECT macro declares some bits of implementation that would be undefined, if it weren't for the moc-generated definitions.

How does moc enter into the picture? When the project is first built, the metabuild tool - either qmake or cmake - scans all the C++ input files for the presence of Q_OBJECT macro. Those that contain it, are given special treatment. In this example project, myobject.h contains Q_OBJECT and is processed through moc into moc_myobject.cpp. The latter is added to the list of sources that are compiled by the C++ compiler. It is, only conceptually, as if you had SOURCES += moc_myobject.cpp in the .pro file. Of course you should never add such a line to the .pro file.

Now notice that the entire implementation of MyObject rests in two files: myobject.h and moc_myobject.cpp. myobject.h can be included in as many translation units as you desire - because there are no out-of-class (standalone) definitions that would violate the one definition rule. The build system treats moc_myobject.cpp as a single, separate translation unit - this is all taken care of for you.

Thus your goal is reached with no effort at all: You have nothing special to do to put the entire implementation of MyObject - save for the bits that moc produces - into the header file. It may prolong the compilation times, but is otherwise innocuous.

A Rule-Violating Approach

It violates the one definition rule, to be exact, and thus yields an invalid C++ program.

Now you might think to get "clever" and forcibly include the moc output into the header file. The qmake/cmake/qbs will be accommodating, and will detect this and won't separately process moc output through the compiler anymore, as you've already done it.

So, suppose that, in the project above, you changed myobject.h to read as follows:

// 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

As it stands, the project will still compile, seemingly fulfilling your goal of having just one file that defines the entirety of MyObject - the bits that you wrote, and the bits that moc generated, both. But it's only due to an unlikely happy circumstance: the contents of moc_*.cpp are still in only one translation unit -

Suppose, now that we add a second source file to our project:

# test.pro
QT       += core
CONFIG   += console
CONFIG   -= app_bundle
TEMPLATE = app
SOURCES += main.cpp test.cpp
HEADERS += myobject.h

// test.cpp
#include "myobject.h"

Not much to it. It should work, even if it doesn't do much, right?

Alas, it won't link. Now the contents of moc_myobject.cpp are a part of two translation units. Since moc_myobject.cpp's insides are full of stand-alone class member definitions, this violates the one definition rule. The rule mandates that stand-alone definitions may only appear in one translation unit within a target. The linker, being the guardian of this rule, rightly complains.

On Including moc Output in the .cpp File

As alluded to in the TL;DR, none of the above precludes explicit inclusion of moc output in source (.cpp) files, in specific circumstances.

Given "foo.h" and "foo.cpp", and a project managed by qmake or cmake, the build system will direct moc to generate up to two outputs:

  1. moc_foo.cpp from foo.h, iff foo.h contains Q_OBJECT macro.

  2. foo.moc from foo.cpp, iff foo.cpp contains #include "foo.moc".

Let's examine in detail why would you want to include either one in a .cpp file.

Including xxx.moc

Sometimes, especially in the days prior to C++11 and Qt 5, it comes handy to declare small helper QObject classes for local use within one translation unit (source file) only.

This also comes handy when writing single-file, self-contained test cases and examples for stackoverflow use.

Suppose you wished someone to demonstrate, in one file, how to invoke a slot from the event loop:

// 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"

Since MyObject is a small class that's only used locally in main.moc, it wouldn't make much sense to put its definition into a separate header file. The #include "main.moc" line will be noticed by qmake/cmake, and main.cpp will be fed through moc, resulting in main.moc. Since main.moc defines members of MyObject, it must be included someplace where MyObject is declared. As the declaration is within main.cpp, you can't have main.moc be a separate translation unit: it won't compile due to MyObject being not declared. The only place where it is declared is within main.cpp, somewhere towards the end. That's why it's a safe bet to always include foo.moc at the end of foo.cpp.

An astute reader now asks: how come moc_foo.cpp gets the declarations of the classes whose members it defines? Quite simply: it explicitly includes the header file it is generated from (here: foo.h). Of course foo.moc can't do that, since it'd break the single definition rule by multiply defining everything in foo.cpp.

Including moc_xxx.cpp

In particularly large Qt projects, you might have - on average - two files and two translation units per each class:

  • MyObject.h and MyObject.cpp are the files you write.
  • MyObject.cpp and moc_MyObject.cpp are the translation units.

It is possible to halve the number of translation units by explicitly including moc_MyObject.cpp somewhere in MyObject.cpp:

// MyObject.cpp
#include "MyObject.h"
#include "moc_MyObject.cpp"
...
拥抱影子 2024-09-12 00:43:51

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

#include <QObject>

class MyClass : public QObject
{
  Q_OBJECT

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

     methodExample()
     {
          // Method content
     }
};

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

I think you can normally declare and implement the class in the header file without using anything special, eg:

#include <QObject>

class MyClass : public QObject
{
  Q_OBJECT

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

     methodExample()
     {
          // Method content
     }
};

After this you add the header file to the pri file and execute qmake again and that's it. You have a class that inherits from qobject and is implemented and declared int he .h file.

明媚殇 2024-09-12 00:43:51

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

Qt 4.8.7

Works.pro:main.cpp

SOURCES += \
    main.cpp

HEADERS += \
    Window.h \
    MyWidget.h

构建

#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

制作

I believe this to be the best way. It's actually how I construct all of my objects now.

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

Build...
qmake Works.pro

make

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文