70

这是我的标题:

#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而不是解决问题。
4

9 回答 9

154

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

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

于 2010-09-06T10:32:11.690 回答
51

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

当编译器看到具有虚函数(直接声明或继承)的类时,它必须为该类生成一个 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 编译器通常遵循的规范,以便它们可以互操作。

于 2014-11-14T11:13:33.193 回答
17

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

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

于 2010-07-13T03:18:12.290 回答
11

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

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

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

于 2010-03-31T19:39:50.367 回答
6

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

于 2010-11-11T21:09:24.577 回答
3

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

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

于 2010-03-31T20:57:41.790 回答
2

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

于 2010-10-08T14:17:49.343 回答
1

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

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

于 2017-01-04T12:22:07.393 回答
0

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

于 2015-08-13T03:05:35.147 回答