سؤال

هل من الممكن إخبار Qt MOC بأنني أرغب في الإعلان عن الفصل وتنفيذه في ملف واحد بدلاً من تقسيمهما إلى ملف .h و.cpp؟

هل كانت مفيدة؟

المحلول 4

أعتقد أن هذا هو أفضل طريقة. إنها في الواقع كيف أقوم ببناء كل الأشياء الخاصة بي الآن.

كيو تي 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"

عليك إعادة تشغيل MOC (make qmake) بعد إضافة #include بيان.

ليرة تركية؛ د

نعم، إذا كنت تتحدث فقط عن الملفات التي تكتبها بنفسك (على عكس تلك التي تم إنشاؤها بواسطة moc).لا تحتاج إلى القيام بأي شيء خاص على الإطلاق.

إذا كنت ترغب في تضمين مخرجات moc بشكل صريح في الملفات التي تكتبها، فهناك حالة واحدة يمكنك فيها ذلك يجب افعل ذلك، وحالة واحدة حيث أنت قد ترغب في القيام بذلك.لنفترض ذلك 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.

لإعادة تشغيل qmake/cmake في Qt Creator، ما عليك سوى النقر بزر الماوس الأيمن على مشروع المستوى الأعلى واختيار قم بتشغيل كميك أو قم بتشغيل cmake من قائمة السياق.

الجواب البسيط

قد يتكون مثال لمشروع Qt القائم على qmake من ثلاثة ملفات، على النحو التالي:

# 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.

على الرغم من أنه يبدو أن التنفيذ الكامل لـ MyObject موجود في ملف الرأس، وهو ليس كذلك حقًا.ال Q_OBJECT يعلن الماكرو عن بعض أجزاء التنفيذ التي قد تكون غير محددة، لولا التعريفات التي تم إنشاؤها بواسطة moc.

كيف يدخل moc في الصورة؟عندما يتم إنشاء المشروع لأول مرة، تقوم أداة metabuild - إما qmake أو cmake - بفحص كافة ملفات الإدخال C++ بحثًا عن وجود Q_OBJECT دقيق.تلك التي تحتوي عليه، يتم منحها معاملة خاصة.في هذا المشروع المثال، myobject.h يتضمن Q_OBJECT ويتم معالجتها من خلال moc إلى moc_myobject.cpp.تتم إضافة الأخير إلى قائمة المصادر التي تم تجميعها بواسطة مترجم C++.إنه، من الناحية المفاهيمية فقط، كما لو كان لديك SOURCES += moc_myobject.cpp في ال .pro ملف. بالطبع لا يجب عليك أبدًا إضافة مثل هذا السطر إلى ملف .pro.

الآن لاحظ أن كامل بداية شئ MyObject يقع في ملفين: myobject.h و moc_myobject.cpp. myobject.h يمكن تضمينها في أي عدد تريده من وحدات الترجمة - لأنه لا توجد تعريفات خارج التصنيف (مستقلة) من شأنها أن تنتهك قاعدة التعريف الواحدة.يعامل نظام البناء moc_myobject.cpp كوحدة ترجمة واحدة ومنفصلة - يتم الاهتمام بكل هذا من أجلك.

وبذلك يتم الوصول إلى هدفك دون أي جهد على الإطلاق:ليس لديك أي شيء خاص لتفعله لوضع التنفيذ الكامل لـ MyObject - حفظ البتات التي ينتجها moc - في ملف الرأس.قد يؤدي ذلك إلى إطالة أوقات التجميع، ولكنه غير ضار بخلاف ذلك.

نهج ينتهك القواعد

إنه ينتهك قاعدة تعريف واحدة, ، على وجه الدقة، وبالتالي ينتج برنامج 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، كلاهما.ولكن هذا فقط بسبب ظرف سعيد غير محتمل:محتويات 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"

ليس كثيرا لذلك.يجب أن تعمل، حتى لو لم تفعل الكثير، أليس كذلك؟

للأسف، لن يتم الارتباط.الآن محتويات moc_myobject.cpp هي جزء من وحدتين للترجمة.منذ moc_myobject.cppإن الدواخل الخاصة بـ مليئة بتعريفات أعضاء الفصل المستقلين، وهذا ينتهك قواعد قاعدة تعريف واحدة.تنص القاعدة على أن التعريفات المستقلة قد تظهر فقط في وحدة ترجمة واحدة ضمن هدف.الرابط، كونه حارس هذه القاعدة، يشكو بحق.

حول تضمين مخرجات moc في ملف .cpp

كما تمت الإشارة إليه في TL;DR، لا يمنع أي مما سبق التضمين الصريح لمخرجات moc في ملفات المصدر (.cpp)، في ظروف محددة.

بالنظر إلى "foo.h" و"foo.cpp"، والمشروع الذي تتم إدارته بواسطة qmake أو cmake، فإن نظام البناء سيوجه moc لتوليد ما يصل إلى اثنين من المخرجات:

  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، يكون من المفيد الإعلان عن فئات QObject المساعدة الصغيرة للاستخدام المحلي ضمن وحدة ترجمة واحدة (الملف المصدر) فقط.

يكون هذا مفيدًا أيضًا عند كتابة حالات اختبار قائمة بذاتها وأمثلة لاستخدام Stackoverflow في ملف واحد.

لنفترض أنك ترغب في أن يوضح شخص ما، في ملف واحد، كيفية استدعاء فتحة من حلقة الحدث:

// 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 الكبيرة بشكل خاص، قد يكون لديك - في المتوسط ​​- ملفان و وحدتا ترجمة لكل فصل:

  • 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 ويتم تنفيذها وإعلانها.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top