Почему мой проект не ссылается, если в файлах Q_OBject есть макросы .cpp?
Вопрос
Этот код собирает, ссылается и работает как задумано:
#include <QApplication>
#include <QListView>
#include "File_List_Model.h"
int main(int c,char**v)
{
QApplication app(c,v);
QStringList list;
list << "a" << "b" << "c";
File_List_Model* model = new File_List_Model;
model->set_entries(list);
QListView* view = new QListView;
view->setModel(model);
view->show();
return app.exec();
}
Но когда я поместил определение класса в .cpp
Файл вместо файлов заголовков, я получаю ошибки на линкере vtable
не был должным образом определен.
#include <QApplication>
#include <QListView>
//#include "File_List_Model.h"
#include "File_List_Proxy.h"
#include <QAbstractItemModel>
#include <QStringList>
class File_List_Model : public QAbstractItemModel
{
Q_OBJECT
private:
QStringList data_;
public:
File_List_Model(QObject *parent = 0) :
QAbstractItemModel(parent)
{
}
int columnCount(const QModelIndex& parent) const
{
return 1;
}
QVariant data(const QModelIndex & index, int role) const
{
switch(role)
{
case Qt::DisplayRole:
return data_[index.row()];
default:
return QVariant();
}
}
QModelIndex index(int row, int column, const QModelIndex & parent) const
{
return createIndex(row,column);
}
QModelIndex parent(const QModelIndex & index) const
{
return QModelIndex();
}
bool set_entries(const QStringList& entries)
{
if (entries.size())
{
beginInsertRows(createIndex(0,0),0,entries.size());
data_ = entries;
endInsertRows();
emit dataChanged(createIndex(0,0),createIndex(0,entries.size()));
return true;
}
else
{
return false;
}
}
int rowCount(const QModelIndex & parent) const
{
return data_.size();
}
};
int main(int c,char**v)
{
QApplication app(c,v);
QStringList list;
list << "a" << "b" << "c";
File_List_Model* model = new File_List_Model;
model->set_entries(list);
File_List_Proxy* proxy = new File_List_Proxy;
proxy->setSourceModel(model);
QListView* view = new QListView;
view->setModel(proxy);
view->show();
return app.exec();
}
//error:
debug/moc_File_List_Model.o:moc_File_List_Model.cpp:(.rdata$_ZTV15File_List_Model[vtable for File_List_Model]+0x44): undefined reference to `File_List_Model::columnCount(QModelIndex const&) const'
debug/moc_File_List_Model.o:moc_File_List_Model.cpp:(.rdata$_ZTV15File_List_Model[vtable for File_List_Model]+0x4c): undefined reference to `File_List_Model::data(QModelIndex const&, int) const'
Кажется, это точно тот же код. Почему он ссылается, когда код находится в заголовках, и он не ссылается иначе?
Решение
QT использует moc
Инструмент для обработки расширений C ++, которые требуют, например, для механизма сигналов. Этот инструмент обрабатывает все файлы заголовка (!) В проекте и генерирует новые исходные файлы, которые содержат код мета-объекта для тех классов, которые содержат Q_OBJECT
макро.
Когда у вас будет определен ваш класс в .cpp
Файл вместо .h
файл moc
не обрабатывает его должным образом.
Посмотри на эта статья Для получения дополнительной информации о компиляторе QT Meta-Object.
Другие советы
Потому что QT запускает MOC в файлах заголовков и не запускает его на источниках.
Линкер жалуется на отсутствие объектного кода, который поступает из компиляции MOC. Это потому, что, хотя MOC обработал исходный файл, его вывод не был составлен в объектный файл.
Для файлов заголовков система сборки предполагает, что они предназначены для включения в несколько единиц перевода и не нарушат Одно правило определения. Анкет Таким образом, вывод MOC может включать в себя файл заголовка и собираться как отдельный блок перевода.
Но если у вас есть Q_OBJECT
макросы в пределах .cpp
Файл, выход MOC не может быть составлен в изоляции: он не будет иметь доступа к объявлениям из ваших .cpp
Файл и, таким образом, не могу скомпилировать! Это также не может включить ваш .cpp
файл, поскольку это нарушит Одно правило определения: Два перевода - вывод MOC и ваш .cpp
Файл - будет определять то же самое.
Вместо этого вам нужно добавить вывод MOC к концу .cpp
файл. Например, если у вас есть O_OBJECT
в main.cpp
, добавлять #include "main.moc"
В конце файла:
// main.cpp
#include <QtCore>
struct Object : QObject {
Q_OBJECT
};
int main() {
Object o;
qDebug() << o.metaObject()->className();
}
#include "main.moc"
// "main.moc" depends on the declaration of Object above!
Выше SSCCE.
Можно утверждать, что, возможно, Qmake/Cmake должен настроить сборку, так что выход MOC автоматически добавляется к .cpp
Файл до того, как он будет отправлен в компилятор. До сих пор эта функция не реализована.