ChatGPT解决这个技术问题 Extra ChatGPT

Qt链接器错误:“未定义对vtable的引用”[重复]

这个问题在这里已经有了答案:Undefined reference to vtable。 Trying to compile a Qt project (21 answers) 5年前关闭。

这是我的标题:

#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 是一个复杂的结构,但即使使用 int 也无法解决问题。

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

M
Michael

每当您向 Q_OBJECT 宏添加新调用时,您都需要再次运行 qmake。您所指的 vtables 问题与此直接相关。

只需运行 qmake ,假设您的代码中没有其他问题,您应该很好。


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

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

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

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

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

在 Qt 类的情况下,它们通常以 Q_OBJECT 宏开头,并且该宏包含声明

virtual const QMetaObject *metaObject() const;

其中,因为它是宏中的第一个虚函数,所以通常是类的第一个虚函数,因此也是它的关键函数。因此,编译器不会在大多数 TU 中发出 vtable,只会在实现 metaObject 的那个中发出。而这个函数的实现是由moc在处理头部时自动编写的。因此,您需要让 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 好主意,添加了一些定义。
D
David

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

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


我今天犯了完全一样的错误!也失去了大约一个小时左右...
知道为什么会这样吗?今天也发生在我身上。
R
Ronny Brendel

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

顺便提一句。 > recive <有错别字

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


当文件已经存在但在运行 qmake 时其中没有任何 Q_OBJECT 引用时,这尤其有用。 qmake 然后不相信它需要运行 moc 并且您最终会遇到 vtable 错误。 make clean 并不总是必要的,但在进行某些结构更改时是必要的。
还要确保 neversocket.h 在你的 pro 文件的 HEADERS 部分。
l
lstipakov

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


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

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

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


删除 receiveMessage 实现是必要的,但它会导致“多重定义的符号”错误。如果基类(在这种情况下为 QObject)具有虚拟析构函数,则所有派生类中的析构函数都是虚拟的。所以这不是问题。
P
Pete

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


S
Sunny127

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

然后添加 .cpp 和 .h 文件。最后工作。


P
Preston

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