是否有可能告诉Qt MOC我想声明该类并在单个文件中实现它而不是将它们分成.h和.cpp文件?
答案 0 :(得分:17)
如果要在cpp文件中声明并实现QObject子类,则必须手动包含moc文件。
例如:(文件main.cpp)
struct SubObject : QObject
{
Q_OBJECT
};
//...
#include "main.moc"
添加make qmake
语句后,您必须重新运行moc(#include
)。
答案 1 :(得分:11)
是的,如果您只谈论自己编写的文件(而不是moc生成的文件)。你根本不需要做任何特别的事情。
如果您希望在您编写的文件中明确包含moc输出,则有一种情况是必须执行此操作,以及可能希望的一种情况去做吧。我们假设在MyObject
中声明了MyObject.h
类,并且您在MyObject.cpp
中给出了它的定义:
MyObject.moc
必须包含在MyObject.cpp
的结尾 iff 您声明任何{{ 1}} Q_OBJECT
内的类。
MyObject.cpp
可以包含在<{1}}中的,以减少项目中翻译单元的数量。它只是一个构建时优化。如果您不这样做,moc_MyObject.cpp
将单独编译。
每次在任何源文件或头文件中添加或删除MyObject.cpp
宏,或者在此类文件中添加或删除moc输出的显式包含时,都必须重新运行qmake / cmake。强>
要在Qt Creator中重新运行qmake / cmake,只需右键单击顶层项目,然后从上下文菜单中选择运行qmake 或运行cmake 。 / p>
基于qmake的Qt项目示例可能包含三个文件,如下所示:
moc_MyObject.cpp
它没有做太多,但它确实有效。除了构建系统链接我们项目的各种库之外,还有两个特定于项目的translation units:Q_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
的整个实施位于两个文件中:.pro
和MyObject
。 myobject.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。该规则要求独立定义只能出现在目标中的一个翻译单元中。作为这条规则的守护者,联系人正确地抱怨。
正如TL; DR中所提到的,在特定情况下,上述任何一项都不排除在源(.cpp)文件中明确包含moc输出。
鉴于&#34; foo.h&#34;和&#34; foo.cpp&#34;,以及由qmake或cmake管理的项目,构建系统将指示moc_myobject.cpp
生成最多两个输出:
moc_myobject.cpp
的 moc
, iff moc_foo.cpp
包含foo.h
宏。
foo.h
的 Q_OBJECT
, iff foo.moc
包含foo.cpp
。
让我们详细研究一下为什么要在.cpp文件中包含任何一个。
有时候,特别是在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
中多次定义所有内容来打破单一定义规则。
在特别大的Qt项目中,平均来说,每个类可能有两个文件和两个翻译单元:
foo.moc
和foo.cpp
是您撰写的文件。MyObject.h
和MyObject.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
请