Q_OBJECT 抛出“undefined reference to vtable”错误

73

我正在使用Qt Creator 2.0.1和Qt 4.7.0(32位)在Windows 7 Ultimate 32位上。

请考虑下面的代码,这是产生错误所需的最小代码:

class T : public QObject, public QGraphicsItem
{
    Q_OBJECT

public:
    T() {}

    QRectF      boundingRect() const {return QRectF();}
    void        paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
                      QWidget *widget) {}
};

int main()
{
    T t;
    return 0;
}

上面的代码片段导致以下链接器错误:
在函数“T”中: 找不到符号引用`T的vtable' 找不到符号引用`T的vtable'
在函数“〜T”中: 找不到符号引用`T的vtable' 找不到符号引用`T的vtable'
如果我注释掉包含`Q_OBJECT'的那一行,它就可以编译通过。 我需要在QGraphicsItem中使用信号和插槽,因此我需要Q_OBJECT
这段代码有什么问题?谢谢。
5个回答

141

MOC生成的单元没有包含在链接过程中,或者根本就没有生成。首先要做的是将类声明放入一个独立的头文件中,也许构建系统没有扫描实现文件。

另一个可能性是,所涉及的类曾经不属于Qt元对象系统(即,它没有Q_OBJECT或者根本没有从QObject继承),因此需要再次运行qmake以创建MOC所需的规则。强制运行qmake的最简单方法是对项目文件进行微不足道的更改以更新其时间戳,例如添加一些空格然后将其删除。如果您使用的是Qt Creator,则只需从项目上下文菜单中选择“运行qmake”即可。


在我的实际实现中,该类被分为头文件和实现文件。错误也存在于那里。 - Donotalo
@Donotalo,MOC在编译期间运行吗?你尝试过清理并重新构建吗? - Sergei Tachenov
4
谢谢您提供的建议。我进行了清理和重建项目,但仍然无法解决问题。接着我编辑了*.pro文件并重新编译,这解决了问题! - Donotalo
4
非常准确的答案-涵盖了我的情况,其中对象最初并不属于Qt元对象系统。 简单编辑文件对我没有用。按照@Donotalo的建议,我必须删除我的.user文件并触摸*.pro文件。 再次运行qMake后,一切都很顺利。 - Alex Hendren
帖子中的答案非常准确。在我的情况下,实现不在头文件中。 - Dean P
显示剩余3条评论

27

如果你想在源文件中定义一个QObject子类,那么你需要添加以下代码行:

#include "file.moc"

在你的类定义之后的某个时刻,源文件名称为file.cpp。当然,你需要重新运行qmake,这样适当的规则才会添加到Makefile中以运行moc

只有在头文件中存在Q_OBJECT时,类定义中才会调用moc。如果是源文件,你需要使用这行额外代码来强制使用moc

我相信类似的问题以前已经被问过,但我找不到它。


非常棒,我已经寻找了几天的方法,不知道这个,QObject文档中没有告诉你这样的东西。 - osirisgothra

8
将你的Q_OBJECT类放在单独的文件中,每个类都有一个.h和一个.cpp文件。在这方面,Qt的元对象宏有点挑剔。
另外,你可以使用QGraphicsObject来实现你的目的。这样可以节省一些时间。
编辑:我看到你正在使用Creator。使用它的New C++ Class功能在New File或Project中创建文件,以“正确的方式”创建文件。 :)

将.h和.cpp文件放在一个单独的文件夹中。 - Sunny127

6

这里是加入了其他问题提供的所有修复程序的工作代码(尝试进行干净编译,这些修复程序有所帮助):

#include <QGraphicsItem>

class T : public QObject, public QGraphicsItem
{
    Q_OBJECT
    Q_INTERFACES(QGraphicsItem) //Required.

public:
    T() {}
    QRectF      boundingRect() const {return QRectF();}
    void        paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
                      QWidget *widget) {}
};

int main(int argc, char *argv[])
{
    T *t = new T;
    return 0;
}

#include "main.moc" // Required.

所以,实际上应该感谢Troubadour和serge_gubenko。

对我来说这很有帮助,因为QObject必须像这样放在第一位。 - edin-m

4

有几个要注意的地方:

  1. 在你的pro文件中添加QT += gui
  2. 确保你只在头文件中定义你派生自QObject的类(编辑:正如Troubadour所指出的,这不是必需的)
  3. 在T类的声明中添加Q_INTERFACES(QGraphicsItem)

以下是一个示例:

t.h:

class T : public QObject, public QGraphicsItem
{
    Q_OBJECT
    Q_INTERFACES(QGraphicsItem)

public:
    T();
    QRectF boundingRect() const;
    void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
};

t.cpp:

T::T() {}

QRectF T::boundingRect() const
{
    return QRectF();
}

void T::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    Q_UNUSED(painter);
    Q_UNUSED(option);
    Q_UNUSED(widget);
}

我已经尝试编译上面的代码,没有遇到任何问题。希望这可以帮助你,问候。

没有必要仅在头文件中定义QObject子类。这只是最常见的做法。有关详细信息,请参阅我的答案。 - Troubadour
在你的pro文件中添加QT += gui,这个简单的步骤帮了我大忙。谢谢。 - iizno

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接