Qt MOC に、クラスを .h と .cpp ファイルに分割するのではなく、クラスを宣言して単一のファイルに実装したいことを伝えることは可能ですか?
4 に答える
cpp ファイルで QObject サブクラスを宣言して実装する場合は、moc ファイルを手動で含める必要があります。
例: (ファイル main.cpp)
struct SubObject : QObject
{
Q_OBJECT
};
//...
#include "main.moc"
ステートメントmake qmake
を追加した後、moc ( ) を再実行する必要があります。#include
TL;DR
はい、(moc によって生成されたものとは対照的に) 自分で作成したファイルについてのみ話している場合は、そうです。特別なことをする必要はまったくありません。
作成するファイルに moc 出力を明示的に含めたい場合は、それを行う必要がある場合と、行う必要がある場合があります。MyObject
class が で宣言され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 を再実行する必要があります。
Qt Creator で qmake/cmake を再実行するには、トップレベル プロジェクトを右クリックし、コンテキスト メニューから[ Run qmake ]または[ Run cmake ] を選択します。
シンプルな答え
qmake ベースの Qt プロジェクトの例は、次のように 3 つのファイルで構成されます。
# 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
大したことではありませんが、確かに有効です。ビルド システムがプロジェクトをリンクするさまざまなライブラリとは別に、2 つのプロジェクト固有の翻訳単位があります: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
2 つのファイルにあることに注意してください。は、必要な数の翻訳単位に含めることができます。これは、1 つの定義規則に違反するクラス外 (スタンドアロン) の定義がないためです。ビルド システムは単一の独立した翻訳単位として扱います。これはすべてユーザーが処理します。myobject.h
moc_myobject.cpp
myobject.h
moc_myobject.cpp
したがって、あなたの目標はまったく努力なしで達成されますMyObject
.mocが生成するビットを除いて-の実装全体をヘッダーファイルに入れるために特別なことは何もありません。コンパイル時間が長くなる可能性がありますが、それ以外は問題ありません。
ルール違反のアプローチ
正確には、 1 つの定義規則に違反しているため、無効な 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 が生成したビットの両方を定義するファイルを 1 つだけにするという目標を達成しているように見えます。しかし、それはありそうもない幸せな状況によるものです: の内容moc_*.cpp
はまだ 1 つの翻訳単位にしかありません -
プロジェクトに 2 つ目のソース ファイルを追加するとします。
# 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
2 つの翻訳単位の一部になりました。moc_myobject.cpp
の内部はスタンドアロンのクラス メンバ定義でいっぱいなので、これは1つの定義規則に違反します。この規則では、スタンドアロンの定義はターゲット内の1 つの翻訳単位にのみ表示されることが義務付けられています。このルールの守護者であるリンカーは、当然のことながら文句を言います。
.cpp ファイルに moc 出力を含める場合
TL;DR で示唆されているように、上記のいずれも、特定の状況でソース (.cpp) ファイルに moc 出力を明示的に含めることを排除するものではありません。
「foo.h」と「foo.cpp」、および qmake または cmake によって管理されるプロジェクトを指定すると、ビルド システムはmoc
最大 2 つの出力を生成するように指示します。
moc_foo.cpp
fromfoo.h
、ifffoo.h
にQ_OBJECT
マクロが含まれています。foo.moc
fromfoo.cpp
、ifffoo.cpp
を含む#include "foo.moc"
.
.cpp ファイルにいずれかを含める理由を詳しく調べてみましょう。
xxx.moc を含む
特に C++11 および Qt 5 より前の時代には、1 つの翻訳単位 (ソース ファイル) 内でのみローカルで使用するための小さなヘルパー QObject クラスを宣言すると便利な場合があります。
これは、stackoverflow で使用する単一ファイルの自己完結型のテスト ケースとサンプルを作成する場合にも便利です。
イベント ループからスロットを呼び出す方法を 1 つのファイルで誰かに説明してもらいたいとします。
// 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 プロジェクトでは、クラスごとに平均して 2 つのファイルと2 つの翻訳単位がある場合があります。
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から継承し、.hファイルに実装および宣言されるクラスがあります。
これが最善の方法であると信じています。それは実際に私がすべてのオブジェクトを構築する方法です。
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
作る