質問

クラスを .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:

  1. MyObject.moc でなければなりません 含まれています 最後にMyObject.cpp もし あなたは何かを宣言します Q_OBJECT 内のクラス MyObject.cpp.

  2. 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 つの出力を生成するには、次のようにします。

  1. moc_foo.cpp から foo.h, もし foo.h 含まれています Q_OBJECT 大きい。

  2. 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ファイルを宣言されていることをクラスを持っています。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top