【问题标题】:Qt Linker Error: "undefined reference to vtable" [duplicate]Qt链接器错误:“未定义对vtable的引用”[重复]
【发布时间】:2011-02-03 02:11:09
【问题描述】:

这是我的标题:

#ifndef BARELYSOCKET_H
#define BARELYSOCKET_H

#include <QObject>
//! The First Draw of the BarelySocket!

class BarelySocket: public QObject
{
    Q_OBJECT

public:
    BarelySocket();
public slots:
    void sendMessage(Message aMessage);
signals:
    void reciveMessage(Message aMessage);

private:
    //   QVector<Message> reciveMessages;
};

#endif // BARELYSOCKET_H

这是我的课:

#include <QTGui>
#include <QObject>
#include "type.h"
#include "client.h"
#include "server.h"

#include "barelysocket.h"

BarelySocket::BarelySocket()
{
    //this->reciveMessages.clear();
    qDebug("BarelySocket::BarelySocket()");
}

void BarelySocket::sendMessage(Message aMessage)
{
}

void BarelySocket::reciveMessage(Message aMessage)
{
}

我收到链接器错误:

undefined reference to 'vtable for BarelySocket'
  • 这意味着我有一个未实现的虚拟方法。但是那里 在我的课堂上没有虚拟方法。
  • 我注释掉了向量,认为这是原因,但是 错误并没有消失。
  • Message 是一个复杂的struct,但即使使用int 也可以 不解决问题。

【问题讨论】:

  • 您是否尝试过从运行qmake 开始的干净构建?如果moc 出于某种原因没有处理您的类的标头,则可能会发生这种情况。
  • 我正在使用 QT Creator。我将所有 cpp 文件复制到一个新的干净 Projekt 中。我删除了我错误编码的插槽实现。比问题消失了。感谢您的帮助!

标签: c++ qt linker-errors vtable qobject


【解决方案1】:

任何时候你添加一个新的调用到 Q_OBJECT 宏,你需要再次运行 qmake。您所指的 vtables 问题与此直接相关。

只要运行 qmake 就可以了,假设您的代码中没有其他问题。

【讨论】:

  • 在 QT Creator 中使用 Build 菜单中的运行 qmake。
  • 谢谢!在命令行中,仅使用make 通常也会更新一些与 qmake 相关的内容,但显然还不够。确实需要显式运行qmake
  • 运行 qmake 对我来说还不够,我不得不删除我的构建文件夹然后重新编译。
  • 它的工作,我只是重新运行 qmake
  • 不适合我。我尝试了所有这些,但没有任何效果。当我删除 Q_OBJECT 宏时我可以成功构建,我认为这不是正确的方法!
【解决方案2】:

我已经看到了很多解决问题的方法,但没有解释为什么会发生,所以就这样吧。

当编译器看到具有虚函数(直接声明或继承)的类时,它必须为该类生成一个 vtable。由于类通常在标题中定义(因此出现在多个翻译单元中),问题是放置 vtable 的位置。

一般来说,可以通过在定义类的每个 TU* 中生成 vtable 来解决问题,然后让链接器消除重复项。由于 ODR** 要求类定义在每次出现时都相同,因此这是安全的。但是,它也会减慢编译速度,使目标文件膨胀,并且需要链接器做更多的工作。

因此,作为一种优化,编译器会在可能的情况下选择一个特定的 TU 来放入 vtable。在常见的 C++ ABI*** 中,这个 TU 是 key function类的实现在其中,其中关键函数是类中声明但未定义的第一个虚成员函数。

在 Qt 类的情况下,它们通常以 Q_OBJECT 宏开头,而这个宏包含声明

virtual const QMetaObject *metaObject() const;

因为它是宏中的第一个虚函数,所以通常是类的第一个虚函数,因此也是它的关键函数。因此,编译器不会在大多数 TU 中发出 vtable,只会在实现 metaObject 的那个中发出。而这个函数的实现是moc在处理header时自动编写的。因此,您需要让moc 处理您的标头以生成新的 .cpp 文件,然后将 .cpp 文件包含在您的编译中。

因此,当您有一个定义 QObject 派生类的新标头时,您需要重新运行 qmake 以便更新您的 makefile 以在新标头上运行 moc 并编译生成的 .cpp 文件。

* TU:翻译单元。 C 和 C++ 中的一个艺术术语,它指的是单个源文件加上从中传递的所有头文件。基本上,编译器在处理单个源文件时看到的东西。

** ODR:一个定义规则。 C++ 标准中的一组规则,用于定义在不同的翻译单元中多次定义事物(函数、类等)时会发生什么。

*** ABI:应用程序二进制接口。对编译时代码组织方式的描述,这是将目标文件链接在一起所必需的。 Common C++ ABI 是 Linux 编译器通常遵循的规范,以便它们可以互操作。

【讨论】:

  • 很棒的文章。我使用的是 CMake 而不是 qmake,而且我经常看到这个错误;了解基本的 C++ vtable 信息却仍然无法弄清楚出了什么问题,这非常令人沮丧。谢谢!
  • @Kyle Strand set(CMAKE_AUTOMOC ON) 不应该足够吗?我有一个 Qt 库,每次都必须运行 qmake 以避免 vtable 错误。这是否意味着cmake 没有按照应有的方式运行moc
  • @JamesHirschorn 你认为这应该足够了,是的。事实上,似乎moc 会为每个构建重新运行,而不考虑输入文件是否实际发生了变化。我不太确定 CMake 的基本问题是什么。
  • 我的 CMake 也有这个问题,是的,我看到 automoc 正在运行,但没有生成文件。如何让automoc扫描头文件?
  • @xcski 好主意,添加了一些定义。
【解决方案3】:

在我为测试某些内容而创建的小“main.cpp”文件中创建了一个小类后,我遇到了这个错误。

在做了一个小时左右之后,我终于将该类从 main.cpp 中移出并放入一个独立的 hpp 文件中,更新了 .pro(项目)文件,然后项目构建得非常好。这可能不是这里的问题,但我认为无论如何这将是有用的信息。

【讨论】:

  • 我今天犯了完全一样的错误!也失去了大约一个小时左右......
  • 知道为什么会这样吗?今天也发生在我身上。
【解决方案4】:

根据经验:经常有一个 qmake && make clean && make 帮助。 我个人认为有时更改发现/缓存效果/whatever-I-don't-know xxxxx。我不能说为什么,但这是我遇到这种错误的第一件事。

顺便说一句。 > recive 有错别字

您忘记在构造函数中调用 QObject 构造函数(在初始化列表中)。 (虽然它不能解决错误)

【讨论】:

  • 这在文件已经存在但在运行 qmake 时没有任何 Q_OBJECT 引用时特别有用。 qmake 然后不相信它需要运行 moc 并且您最终会遇到 vtable 错误。 make clean 并不总是必要的,但在进行某些结构更改时是必要的。
  • 还要确保在你的 pro 文件中的 HEADERS 部分中找到了 baresocket.h。
【解决方案5】:

对我来说,我从构建日志中注意到 moc 没有被调用。清洁所有没有帮助。所以我删除了 .pro.user,重新启动了 IDE,它成功了。

【讨论】:

  • 这正是发生在我身上的事。当我添加一个不是 QObject 父级的类时触发了这个 QtCreator 错误,但后来手动修复了它。我做了grep Class Project.user,但它丢失了文件。所以我只是删除了 .user 文件,重建了所有内容,错误就消失了
【解决方案6】:

信号不能有实现(这将由 Qt 生成)。从 .cpp 文件中删除 reciveMessage 实现。这可能会解决您的问题。

我看到的另一件事:由于BarelySocket 类继承自 QObject,它必须有一个虚拟析构函数以避免在销毁过程中出现问题。对于从其他类继承的所有类都必须这样做。

【讨论】:

  • 删除receiveMessage 实现是必要的,但它会导致“多重定义的符号”错误。如果基类(本例中为 QObject)具有虚拟析构函数,则所有派生类中的析构函数都自动为虚拟的。所以这不是问题。
【解决方案7】:

当您从 QOBject 派生类(并使用 Q_OBJECT 宏)时,不要忘记专门定义和创建构造函数和析构函数类。仅使用编译器默认构造函数/析构函数是不够的。关于清理/运行 qmake(并清除 moc_ 文件)的建议仍然适用。 这解决了我的类似问题。

【讨论】:

    【解决方案8】:

    我在这个错误时间里苦苦挣扎。通过将 .cpp 和 .h 文件放在单独的文件夹 (!!) 中解决了这个问题。 然后在 .pro 文件中添加文件夹: INCLUDEPATH += $${_PRO_FILE_PWD_}/../MyClasses/CMyClassWidget

    然后添加 .cpp 和 .h 文件。 终于成功了。

    【讨论】:

      【解决方案9】:

      我发现您可能会看到这个的另一个原因 - 因为qmake 会解析您的类文件,如果您以非标准方式修改它们,您可能会收到此错误。就我而言,我有一个继承自 QDialog 的自定义对话框,但我只希望在为 Linux 而不是 Windows 或 OSX 构建时编译和运行它。我只是将 #ifdef __linux__ 类排除在外,所以它没有编译,但在 Linux 中,即使定义了 __linux__,它也会抛出 qmake

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多