这个问题在这里已经有了答案: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
出于某种原因未处理您的类的标头,则可能会发生这种情况。
每当您向 Q_OBJECT 宏添加新调用时,您都需要再次运行 qmake。您所指的 vtables 问题与此直接相关。
只需运行 qmake ,假设您的代码中没有其他问题,您应该很好。
我已经看到了很多解决问题的方法,但没有解释为什么会发生这种情况,所以就这样吧。
当编译器看到具有虚函数(直接声明或继承)的类时,它必须为该类生成一个 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 编译器通常遵循的规范,以便它们可以互操作。
set(CMAKE_AUTOMOC ON)
不应该足够吗?我有一个 Qt 库,每次都必须运行 qmake
以避免 vtable 错误。这是否意味着 cmake
没有按应有的方式运行 moc
?
moc
,而不考虑输入文件是否实际更改。我不太确定 CMake 的基本问题是什么。
在我为测试某些东西而创建的小“main.cpp”文件中创建了一个小类后,我遇到了这个错误。
经过一个小时左右的测试,我终于将该类从 main.cpp 中移出并放入一个独立的 hpp 文件中,更新了 .pro(项目)文件,然后该项目构建得非常好。这可能不是这里的问题,但我认为无论如何这将是有用的信息。
根据经验:通常是 qmake && make clean && make 有帮助。我个人认为有时更改发现/缓存效果/whatever-I-don't-know xxxxx。我不能说为什么,但是当我遇到这种错误时,这是我做的第一件事。
顺便提一句。 > recive <有错别字
您忘记在构造函数中调用 QObject 构造函数(在初始化列表中)。 (虽然它不能解决错误)
对我来说,我从构建日志中注意到没有调用 moc。清洁所有没有帮助。所以我删除了 .pro.user,重新启动了 IDE,它就成功了。
grep Class Project.user
,但它丢失了文件。所以我只是删除了 .user 文件,重建了所有内容,错误就消失了
信号不能有实现(这将由 Qt 生成)。从您的 .cpp 文件中删除 reciveMessage
实现。这可能会解决您的问题。
我看到的另一件事:由于 BarelySocket
类继承自 QObject 它必须有一个虚拟析构函数以避免在销毁过程中出现问题。这必须对从其他类继承的所有类进行。
当您从 QOBject 派生一个类时(并使用 Q_OBJECT 宏),不要忘记专门定义和创建构造函数和析构函数类。仅使用编译器默认构造函数/析构函数是不够的。关于清理/运行 qmake(并清除 moc_ 文件)的建议仍然适用。这解决了我的类似问题。
我在这个错误时间里苦苦挣扎。通过将 .cpp 和 .h 文件放在单独的文件夹 (!!) 中解决了这个问题。然后在 .pro 文件中添加文件夹: INCLUDEPATH += $${_PRO_FILE_PWD_}/../MyClasses/CMyClassWidget
然后添加 .cpp 和 .h 文件。最后工作。
我找到了您可能会看到此错误的另一个原因 - 因为如果您以非标准方式修改了类文件,qmake
会解析它们,您可能会收到此错误。在我的例子中,我有一个继承自 QDialog 的自定义对话框,但我只希望在为 Linux 而不是 Windows 或 OSX 构建时编译和运行它。我只是 #ifdef __linux__
了这个类,所以它没有编译,但在 Linux 中,即使定义了 __linux__
,它也会抛出 qmake
。
不定期副业成功案例分享
make
也会更新一些与 qmake 相关的内容,但显然还不够。确实需要显式运行qmake
。