Почему мой проект не связывается, если в файлах .cpp есть макросы Q_OBJECT? - PullRequest
1 голос
/ 29 ноября 2011

Этот код компилируется, связывается и работает как задумано:

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

Кажется, что это тот же самый код.Почему он связывается, когда код находится в заголовках, и не связывается иначе?

Ответы [ 3 ]

3 голосов
/ 29 ноября 2011

Qt использует инструмент moc для обработки расширений C ++, которые требуются, например, для механизма сигналов-слотов. Этот инструмент обрабатывает все заголовочные (!) Файлы в проекте и генерирует новые исходные файлы, которые содержат мета-объектный код для тех классов, которые содержат макрос Q_OBJECT.

Если ваш класс определен в файле .cpp вместо .h, файл moc не сможет правильно его обработать.

Ознакомьтесь с этой статьей для получения дополнительной информации о Qt Meta-Object Compiler.

0 голосов
/ 14 июля 2016

Компоновщик жалуется на отсутствие объектного кода, который получается при компиляции вывода moc.Это связано с тем, что хотя moc обработал исходный файл, его выходные данные не были скомпилированы в объектный файл.

Для заголовочных файлов система сборки предполагает, что они предназначены для включения в несколько модулей перевода, и не будет нарушать одно определение правила .Таким образом, вывод moc может включать в себя файл заголовка и компилироваться как автономный модуль перевода.

Но если у вас есть какие-либо макросы Q_OBJECT в файле .cpp, вывод moc не может быть скомпилированизоляция: он не будет иметь доступа к объявлениям из вашего .cpp файла и, следовательно, не сможет скомпилировать!Он также не может включать ваш файл .cpp, поскольку это нарушит правило one Definition : две единицы перевода - вывод 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 перед его отправкой компилятору.Пока что эта функция не реализована.

0 голосов
/ 29 ноября 2011

Поскольку Qt запускает moc для заголовочных файлов, а не для исходников.

...