質問
クラスを .h と .cpp ファイルに分割するのではなく、単一のファイルで宣言して実装したいことを Qt MOC に伝えることはできますか?
解決 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();
}
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
メイク
他のヒント
あなたが宣言したいとQObjectのサブクラスあなたの中のcppファイルを実装する場合は、手動MOCファイルをインクルードする必要があります。
例えば、:(ファイルmain.cppに)
struct SubObject : QObject
{
Q_OBJECT
};
//...
#include "main.moc"
あなたはmake qmake
ステートメントを追加した後MOC(#include
)を再実行する必要があります。
TL;DR
はい、(moc によって生成されたファイルではなく) 自分で作成したファイルのみについて話しているのであれば、そうです。特別なことをする必要はまったくありません。
作成するファイルに moc 出力を明示的に含めたい場合は、次のようなケースがあります。 しなければならない それをやると、次のようなケースが 1 つあります。 かもしれない それをしたいと思っています。仮定してみましょう MyObject
クラスはで宣言されています 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 を再実行するには、トップレベルのプロジェクトを右クリックし、 qmakeを実行する または 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 つのファイルに分かれています。 myobject.h
そして moc_myobject.cpp
. myobject.h
1 つの定義ルールに違反するクラス外 (スタンドアロン) 定義がないため、必要な数の翻訳単位に含めることができます。ビルドシステムが扱うのは、 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
現状でもプロジェクトはコンパイルされ、全体を定義するファイルを 1 つだけ用意するという目標は達成されているように見えます。 MyObject
- あなたが書いたビットと moc が生成したビット、両方。しかし、それはただ、ありそうもない幸せな状況によるものです。の内容 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 つの翻訳単位 ターゲット内で。リンカーは、このルールの守護者として、当然のことながら不平を言います。
moc 出力を .cpp ファイルに含めるについて
TL;DR でほのめかされているように、上記のいずれも、特定の状況においてソース (.cpp) ファイルに moc 出力を明示的に含めることを妨げるものではありません。
「foo.h」と「foo.cpp」、および qmake または cmake によって管理されるプロジェクトを指定すると、ビルド システムは次のように指示します。 moc
最大 2 つの出力を生成するには、次のようにします。
moc_foo.cpp
からfoo.h
, もしfoo.h
含まれていますQ_OBJECT
大きい。foo.moc
からfoo.cpp
, もしfoo.cpp
含まれています#include "foo.moc"
.
どちらかを .cpp ファイルに含める理由を詳しく調べてみましょう。
xxx.mocを含む
場合によっては、特に C++11 や Qt 5 が登場する前の時代には、1 つの翻訳単位 (ソース ファイル) 内でのみローカルで使用する小さなヘルパー QObject クラスを宣言すると便利です。
これは、スタックオーバーフローで使用する単一ファイルの自己完結型テスト ケースやサンプルを作成するときにも便利です。
イベント ループからスロットを呼び出す方法を 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を継承しが実装され、彼int型.hファイルを宣言されていることをクラスを持っています。