如果 .cpp 文件中有 Q_OBJECT 宏,为什么我的项目无法链接?

发布于 2024-12-18 07:31:38 字数 2922 浏览 4 评论 0原文

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

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

这似乎是完全相同的代码。为什么当代码位于标题中时它会链接,而其他情况下它不会链接?

This code compiles, links and works as intended:

#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();
} 

But when I put the class definition in .cpp file instead of header files, I get linker errors stating that vtable was not properly defined.

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

This seems to be exactly the same code. Why does it link when the code is in headers and it doesn't link otherwise?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

姐不稀罕 2024-12-25 07:31:38

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

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

有关 Qt 元对象编译器的更多信息,请参阅本文

Qt uses moc tool to handle C++ extensions that a required, for example, for signals-slots mechanism. This tool processes all header (!) files in the project and generates new source files that contain meta-object code for those classes that contain Q_OBJECT macro.

When you have your class defined in .cpp file instead of .h file moc fails to process it properly.

Have a look at this article for more information about Qt Meta-Object Compiler.

机场等船 2024-12-25 07:31:38

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

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

但是,如果 .cpp 文件中有任何 Q_OBJECT 宏,则无法单独编译 moc 输出:它将无法访问 中的声明>.cpp 文件,因此无法编译!它还不能包含您的 .cpp 文件,因为这会违反单一定义规则 :两个翻译单元 - 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 文件中。到目前为止,该功能尚未实现。

The linker is complaining about missing the object code that comes from compiling moc output. That's because although moc has processed the source file, its output was not compiled into an object file.

For header files, the build system assumes that they are meant for inclusion in multiple translation units and won't violate the one definition rule. Thus moc output can include the header file, and get compiled as a stand-alone translation unit.

But, if you have any Q_OBJECT macros within a .cpp file, the moc output cannot be compiled in isolation: it won't have access to the declarations from your .cpp file and thus can't compile! It also can't include your .cpp file, since that would violate the one definition rule: two translation units - moc output and your .cpp file - would define the same stuff.

Instead, you need to append moc's output to the end of the .cpp file. E.g., if you have O_OBJECT in main.cpp, add #include "main.moc" at the end of the file:

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

The above is an SSCCE.

One could argue that perhaps qmake/cmake should set up the build so that moc output is automatically appended to the .cpp file before it's sent to the compiler. So far, that feature is not implemented.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文