【问题标题】:Why my project doesn't link if there are Q_OBJECT macros in .cpp files?如果 .cpp 文件中有 Q_OBJECT 宏,为什么我的项目没有链接?
【发布时间】:2011-11-29 07:05:59
【问题描述】:

此代码按预期编译、链接和工作:

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

这似乎是完全相同的代码。为什么代码在header的时候链接,其他地方不链接?

【问题讨论】:

    标签: qt qmake moc


    【解决方案1】:

    Qt 使用moc 工具来处理所需的 C++ 扩展,例如信号槽机制。此工具处理项目中的所有头 (!) 文件,并为包含 Q_OBJECT 宏的那些类生成包含元对象代码的新源文件。

    当您在.cpp 文件而不是.h 文件中定义您的类时,moc 无法正确处理它。

    查看this article 了解有关 Qt 元对象编译器的更多信息。

    【讨论】:

    • qmake 和 cmake Qt 支持在项目中列出的所有包含 Q_OBJECT 宏的源文件上调用 moc。无论文件的扩展名如何,都会发生这种情况。在问题的情况下,moc 输出未传递给编译器,因为 qmake/cmake 无法自动执行此操作。
    【解决方案2】:

    因为 Qt 在头文件上运行 moc 而不在源文件上运行。

    【讨论】:

    • 那是错误的:qmake 和 cmake 在所有包含 Q_OBJECT 宏的源文件上运行 moc。这里没有做的是在 moc 的输出上运行编译器。
    【解决方案3】:

    链接器抱怨缺少来自编译 moc 输出的目标代码。这是因为虽然 moc 已经处理了源文件,但它的输出并没有编译成目标文件。

    对于头文件,构建系统假定它们旨在包含在多个翻译单元中,并且不会违反one definition rule。因此 moc 输出可以包含头文件,并被编译为独立的翻译单元。

    但是,如果您在 .cpp 文件中有任何 Q_OBJECT 宏,则无法单独编译 moc 输出:它无法访问您的 .cpp 文件中的声明,因此无法编译!它也不能包含您的 .cpp 文件,因为这将违反 one definition rule:两个翻译单元 - moc 输出和您的 .cpp 文件 - 将定义相同的内容。

    相反,您需要将 moc 的输出附加到 .cpp 文件的末尾。例如,如果您在main.cpp 中有O_OBJECT,则在文件末尾添加#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 文件。到目前为止,该功能尚未实现。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-11-25
      • 2022-01-25
      • 2021-11-27
      • 2020-09-09
      相关资源
      最近更新 更多