19

Qt MOC に、クラスを .h と .cpp ファイルに分割するのではなく、クラスを宣言して単一のファイルに実装したいことを伝えることは可能ですか?

4

4 に答える 4

21

cpp ファイルで QObject サブクラスを宣言して実装する場合は、moc ファイルを手動で含める必要があります。

例: (ファイル main.cpp)

struct SubObject : QObject
{
    Q_OBJECT
};

//...

#include "main.moc"

ステートメントmake qmakeを追加した後、moc ( ) を再実行する必要があります。#include

于 2010-06-09T11:36:36.327 に答える
16

TL;DR

はい、(moc によって生成されたものとは対照的に) 自分で作成したファイルについてのみ話している場合は、そうです。特別なことをする必要はまったくありません。

作成するファイルに moc 出力を明示的に含めたい場合は、それを行う必要がある場合と、行う必要がある場合がありますMyObjectclass が で宣言されMyObject.h、その定義が で与えられていると仮定しましょうMyObject.cpp:

  1. MyObject.moc 内でクラスを宣言する場合は、 の最後に含める必要があります。MyObject.cpp Q_OBJECTMyObject.cpp

  2. 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.cppmoc_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 ファイルに追加するべきではありません。

の実装全体がとのMyObject2 つのファイルにあることに注意してください。は、必要な数の翻訳単位に含めることができます。これは、1 つの定義規則に違反するクラス外 (スタンドアロン) の定義がないためです。ビルド システムは単一の独立した翻訳単位として扱います。これはすべてユーザーが処理します。myobject.hmoc_myobject.cppmyobject.hmoc_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.cpp2 つの翻訳単位の一部になりました。moc_myobject.cppの内部はスタンドアロンのクラス メンバ定義でいっぱいなので、これは1つの定義規則に違反します。この規則では、スタンドアロンの定義はターゲット内の1 つの翻訳単位にのみ表示されることが義務付けられています。このルールの守護者であるリンカーは、当然のことながら文句を言います。

.cpp ファイルに moc 出力を含める場合

TL;DR で示唆されているように、上記のいずれも、特定の状況でソース (.cpp) ファイルに moc 出力を明示的に含めることを排除するものではありません。

「foo.h」と「foo.cpp」、および qmake または cmake によって管理されるプロジェクトを指定すると、ビルド システムはmoc最大 2 つの出力を生成するように指示します。

  1. moc_foo.cppfrom foo.hiff foo.hQ_OBJECTマクロが含まれています。

  2. foo.mocfrom foo.cppiff foo.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.mocMyObjectmain.cppfoo.mocfoo.cpp

賢明な読者は、次のように問いかけます:moc_foo.cppメンバーが定義されているクラスの宣言を取得するにはどうすればよいでしょうか? 簡単に言えば、生成元のヘッダー ファイルを明示的にインクルードします (ここではfoo.h)。もちろんfoo.moc、それはできません。なぜなら、 のすべてを複数定義することによって、単一の定義規則が破られるからfoo.cppです。

moc_xxx.cppを含む

特に大規模な Qt プロジェクトでは、クラスごとに平均して 2 つのファイルと2 つの翻訳単位がある場合があります。

  • MyObject.hおよびMyObject.cppは、作成するファイルです。
  • MyObject.cppmoc_MyObject.cppは翻訳単位です。

moc_MyObject.cppのどこかに明示的に含めることで、翻訳単位の数を半分にすることができますMyObject.cpp

// MyObject.cpp
#include "MyObject.h"
#include "moc_MyObject.cpp"
...
于 2015-03-26T20:01:47.493 に答える
3

通常、特別なことを何も使用せずに、ヘッダーファイルでクラスを宣言して実装できると思います。例:

#include <QObject>

class MyClass : public QObject
{
  Q_OBJECT

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

     methodExample()
     {
          // Method content
     }
};

この後、ヘッダーファイルをpriファイルに追加し、qmakeを再度実行します。これで完了です。qobjectから継承し、.hファイルに実装および宣言されるクラスがあります。

于 2010-06-13T09:37:56.677 に答える
-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

作る

于 2011-01-11T23:18:00.647 に答える