Pergunta

É possível dizer ao QT MOC que eu gostaria de declarar a classe e implementá -la em um único arquivo, em vez de dividi -los em um arquivo .h e .cpp?

Foi útil?

Solução 4

Eu acredito que essa é a melhor maneira. Na verdade, é como eu construo todos os meus objetos agora.

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

Build ... Qmake Works.pro

faço

Outras dicas

Se você deseja declarar e implementar uma subclasse QObject em seu arquivo CPP, você deve incluir manualmente o arquivo MOC.

Por exemplo: (arquivo main.cpp)

struct SubObject : QObject
{
    Q_OBJECT
};

//...

#include "main.moc"

Você tem que executar executar executar o MOC (make qmake) depois de adicionar o #include declaração.

Tl; dr

Sim, se você está falando apenas sobre os arquivos que você escreve (em oposição aos gerados pelo MOC). Você não precisa fazer nada de especial.

Se você deseja incluir a saída MOC explicitamente nos arquivos que você escreve, há um caso em que você devo Faça isso, e um caso em que você poderia desejo fazer isso. Vamos supor que MyObject A classe é declarada em MyObject.h e sua definição é dada em MyObject.cpp:

  1. MyObject.moc devemos ser incluído no final do MyObject.cpp iff você declara qualquer Q_OBJECT classes dentro MyObject.cpp.

  2. moc_MyObject.cpp pode ser incluído qualquer lugar dentro MyObject.cpp Para reduzir pela metade o número de unidades de tradução em seu projeto. É apenas uma otimização em tempo de construção. Se você não fizer isso, moc_MyObject.cpp será compilado separadamente.

Toda vez que você adiciona ou remove Q_OBJECT Macro de qualquer arquivo de origem ou cabeçalho, ou você adiciona ou remove inclusões explícitas da saída MOC nesses arquivos, você deve executar novamente o QMake/cmake.

Para executar novamente o QMake/cmake no QT Creator, basta clicar com o botão direito do mouse no projeto Toplevel e selecionar Execute QMake ou Execute cmake No menu de contexto.

A resposta simples

Um exemplo de projeto QT baseado em QMake pode consistir em três arquivos, como segue:

# 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

Não faz muito, mas certamente é válido. Além das várias bibliotecas com as quais o sistema de construção vincula nosso projeto, existem dois projetos específicos unidades de tradução: main.cpp e moc_myobject.cpp.

Mesmo que pareça que toda a implementação de MyObject está no arquivo de cabeçalho, realmente não é. o Q_OBJECT Macro declara alguns bits de implementação que não seriam definidos, se não fossem as definições geradas por MOC.

Como o MOC entra na imagem? Quando o projeto é construído pela primeira vez, a ferramenta de metabuild - qmake ou cmake - digitaliza todos os arquivos de entrada C ++ quanto à presença de Q_OBJECT macro. Aqueles que o contêm recebem tratamento especial. Neste exemplo de projeto, myobject.h contém Q_OBJECT e é processado através do MOC em moc_myobject.cpp. Este último é adicionado à lista de fontes compiladas pelo compilador C ++. É, apenas conceitualmente, como se você tivesse SOURCES += moc_myobject.cpp no .pro Arquivo. É claro que você nunca deve adicionar essa linha ao arquivo .pro.

Agora observe que o inteira implementação de MyObject repousa em dois arquivos: myobject.h e moc_myobject.cpp. myobject.h Pode ser incluído em quantas unidades de tradução desejar-porque não há definições fora da classe (independentes) que viole a regra de uma definição. O sistema de construção trata moc_myobject.cpp Como uma unidade de tradução única e separada - tudo isso é resolvido para você.

Assim, seu objetivo é alcançado sem nenhum esforço: você não tem nada de especial para fazer para colocar toda a implementação de MyObject - Salve para os bits que o MOC produz - no arquivo de cabeçalho. Pode prolongar os tempos de compilação, mas é inócuo.

Uma abordagem violadora de regras

Viola o uma regra de definição, para ser exato e, portanto, produz um programa C ++ inválido.

Agora você pode pensar em obter "inteligente" e incluir à força a saída MOC no arquivo de cabeçalho. O QMake/Cmake/QBs será acomodado, e detectará isso e não processará mais a saída MOC separadamente através do compilador, como você já o fez.

Então, suponha que, no projeto acima, você mudou myobject.h para ler o seguinte:

// 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

Tal como está, o projeto ainda compilará, aparentemente cumprindo seu objetivo de ter apenas um arquivo que define a totalidade de MyObject - Os bits que você escreveu e os bits que o MOC geraram, ambos. Mas é apenas devido a uma circunstância feliz improvável: o conteúdo de moc_*.cpp ainda estão em apenas uma unidade de tradução -

Suponha, agora que adicionamos um segundo arquivo de origem ao nosso projeto:

# test.pro
QT       += core
CONFIG   += console
CONFIG   -= app_bundle
TEMPLATE = app
SOURCES += main.cpp test.cpp
HEADERS += myobject.h

// test.cpp
#include "myobject.h"

Não muito para isso. Deve funcionar, mesmo que não faça muito, certo?

Infelizmente, não vai vincular. Agora o conteúdo de moc_myobject.cpp fazem parte de duas unidades de tradução. Desde moc_myobject.cppO interior está cheio de definições de membros de classe independentes, isso viola o uma regra de definição. A regra exige que as definições independentes só possam aparecer em uma unidade de tradução dentro de um alvo. O ligante, sendo o guardião desta regra, reclama corretamente.

Ao incluir a saída MOC no arquivo .cpp

Como mencionado no TL; DR, nenhum dos acima impede a inclusão explícita da saída MOC em arquivos de origem (.CPP), em circunstâncias específicas.

Dado "foo.h" e "foo.cpp", e um projeto gerenciado por qmake ou cmake, o sistema de construção direcionará moc Para gerar até duas saídas:

  1. moc_foo.cpp a partir de foo.h, iff foo.h contém Q_OBJECT macro.

  2. foo.moc a partir de foo.cpp, iff foo.cpp contém #include "foo.moc".

Vamos examinar em detalhes por que você deseja incluir um em um arquivo .cpp.

Incluindo xxx.moc

Às vezes, especialmente nos dias anteriores aos C ++ 11 e Qt 5, é útil declarar pequenas classes de QOBject para uso local em uma unidade de tradução (arquivo de origem) apenas.

Isso também é útil ao escrever casos de teste e exemplos independentes para uso do StackOverflow.

Suponha que você desejasse alguém para demonstrar, em um arquivo, como invocar um slot do loop do evento:

// 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"

Desde MyObject é uma classe pequena que só é usada localmente em main.moc, não faria muito sentido colocar sua definição em um arquivo de cabeçalho separado. o #include "main.moc" A linha será notada por Qmake/cmake e main.cpp será alimentado através do MOC, resultando em main.moc. Desde main.moc define membros de MyObject, deve ser incluído em algum lugar onde MyObject é declarado. Como a declaração está dentro main.cpp, você não pode ter main.moc ser uma unidade de tradução separada: não será compilada devido a MyObject sendo não declarado. O único lugar onde é declarado está dentro main.cpp, em algum lugar no final. É por isso que é uma aposta segura sempre incluir foo.moc no fim de foo.cpp.

Um leitor astuto agora pergunta: como é moc_foo.cpp Recebe as declarações das classes cujos membros ele define? Simplesmente: inclui explicitamente o arquivo de cabeçalho de que é gerado (aqui: foo.h). É claro foo.moc Não posso fazer isso, pois quebraria a regra de definição única, multiplicando tudo em foo.cpp.

Incluindo moc_xxx.cpp

Em projetos de QT particularmente grandes, você pode ter - em média - dois arquivos e Duas unidades de tradução por cada classe:

  • MyObject.h e MyObject.cpp são os arquivos que você escreve.
  • MyObject.cpp e moc_MyObject.cpp são as unidades de tradução.

É possível reduzir pela metade o número de unidades de tradução, incluindo explicitamente moc_MyObject.cpp Em algum lugar em MyObject.cpp:

// MyObject.cpp
#include "MyObject.h"
#include "moc_MyObject.cpp"
...

Eu acho que você normalmente pode declarar e implementar a classe no arquivo de cabeçalho sem usar nada especial, por exemplo:

#include <QObject>

class MyClass : public QObject
{
  Q_OBJECT

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

     methodExample()
     {
          // Method content
     }
};

Depois disso, você adiciona o arquivo de cabeçalho ao arquivo PRI e execute o QMake novamente e é isso. Você tem uma classe que herda do QObject e é implementada e declarada arquivo .h.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top