Qt MOC com implementações dentro dos arquivos de cabeçalho?
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?
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
:
MyObject.moc
devemos ser incluído no final doMyObject.cpp
iff você declara qualquerQ_OBJECT
classes dentroMyObject.cpp
.moc_MyObject.cpp
pode ser incluído qualquer lugar dentroMyObject.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.cpp
O 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:
moc_foo.cpp
a partir defoo.h
, ifffoo.h
contémQ_OBJECT
macro.foo.moc
a partir defoo.cpp
, ifffoo.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
eMyObject.cpp
são os arquivos que você escreve.MyObject.cpp
emoc_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.