Qt:缺少vtable通常意味着第一个非内联虚拟成员函数没有定义

时间:2014-05-11 17:50:37

标签: qt clang

这一切都有很多threads。似乎没有人适合我的账单。我的代码中出现以下链接器错误:

Undefined symbols for architecture x86_64:
  "vtable for MSFSPlugin::MSFSPluginImpl", referenced from:
      MSFSPlugin::MSFSPluginImpl::MSFSPluginImpl(QObject*) in MSFSPlugin.o
      MSFSPlugin::MSFSPluginImpl::~MSFSPluginImpl() in MSFSPlugin.o
  NOTE: a missing vtable usually means the first non-inline virtual member function has no definition.
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

它应该是显而易见的 - 缺少vtable通常意味着第一个非内联虚拟成员函数没有定义。但是,我不知道我错过了什么:

我在MSFSPlugin.h

中有这个类缩减
class MSFSPlugin
    :
    public QObject,
    public IMediaSource
{
    Q_OBJECT
    Q_INTERFACES(IMediaSource)
    ...
protected:
    class MSFSPluginImpl;
    MSFSPluginImpl* mImpl;

}

然后在MSFSPlugin.cpp中,我有以下内容:

class MSFSPlugin::MSFSPluginImpl : public QThread
{
    Q_OBJECT
    public:
        MSFSPluginImpl(QObject *parent = 0);
        virtual ~MSFSPluginImpl();

        QString     getSourceDirectory() const;
        void        setSourceDirectory(QString sourceDirectory);

    signals:
        void        loadDirectoryFinished(bool success);

    protected:
        QString     mSourceDirectory;
};

其次是定义:

MSFSPlugin::MSFSPluginImpl::MSFSPluginImpl(QObject *parent) : QThread(parent)
{
}

MSFSPlugin::MSFSPluginImpl::~MSFSPluginImpl()
{
}

QString MSFSPlugin::MSFSPluginImpl::getSourceDirectory() const
{
    return mSourceDirectory;
}

void MSFSPlugin::MSFSPluginImpl::setSourceDirectory(QString sourceDirectory)
{
    mSourceDirectory = sourceDirectory;
}

...

简而言之,我不认为我错过了任何非内联虚拟成员函数定义。我正在使用:

Apple LLVM version 5.1 (clang-503.0.40) (based on LLVM 3.4svn)
Target: x86_64-apple-darwin13.1.0
Thread model: posix

其他信息:

在我的moc_MSFSPlugin.cpp中,我没有看到类Q_OBJECT的自动生成的MSFSPlugin::MSFSPluginImpl相关代码,它间接地(通过QThread)派生自QObject。具体来说,我没有看到在该类(loadDirectoryFinished)中声明的信号生成的代码。这可能是问题吗?

编辑1: 如果我从Q_OBJECT的分解中注释掉MSFSPlugin::MSFSPluginImpl,那么错误就会消失,但我会失去信号功能。

编辑2: 我看到moc只对头文件进行操作。这可能与我的QObject派生类被声明&在.cpp文件中定义?

2 个答案:

答案 0 :(得分:33)

假设我们正在处理qmake

黄金法则是:

  1. 确保所有Q_OBJECT派生类的定义中都存在QObject宏。
  2. 请务必在头文件 中声明QObject - 派生类。
  3. 确保所有头文件都列在HEADERS=列表的.pro文件中。
  4. 每次将qmake添加到其中一个班级或修改Q_OBJECT文件时,请运行.pro

  5. 解释

      

    编辑1:如果我从MSFSPlugin :: MSFSPluginImpl的declation注释掉Q_OBJECT,那么错误就会消失,但是我失去了信号功能。

    烨。声明信号,广告位,可调用内容,Q_OBJECT,翻译,属性,枚举和方法内省等需要 qobject_cast

    #1黄金法则来自于没有 Q_OBJECT你无法在课堂上使用qobject_cast这样的内容;如果您使用(甚至间接)内省设施,例如debug an object hierarchydump all active connections to an object,那么您的类的对象将显示真实的类名(而不是超类的一个);等

    Q_OBJECT做了两件事:

    1. 它告诉builsystems添加对moc 的调用,其作用是为类生成一些额外的代码。此代码将提供上面列出的所有设施;
    2. 因为它是一个普通的C ++宏,当编译 it expands to a few declarations 时,包括几个虚拟:qt_metacall()metaObject()moc将为这些虚拟机生成实现。
    3. 您获得的错误是声明虚拟的典型症状(因为宏已在您的代码中展开)但moc未运行,您有一些未实现的虚拟将导致链接失败。< / p>

      使用gcc和GNU ld,你会得到一个更加神秘的错误,大概是undefined reference to vtable for ClassName。当然,谷歌搜索这些错误会立即告诉你如何解决这个问题。

        

      编辑2:我看到moc仅对头文件进行操作。这可能与我的QObject派生类被声明&amp;在.cpp文件中定义?

      所以,问题是:为什么moc没有在包含Q_OBJECT宏的类定义的文件上运行?

      当我们使用qmake生成Makefile时,qmake将扫描HEADERS变量中列出的所有头文件。当它发现一个头包含一个带有Q_OBJECT宏的类定义时,它还会发出指令(在Makefile中)以在该头上运行moc,编译moc的输出和将结果对象链接到最终目标中。

      我们在这里有规则#2,#3,#4。

      #2告诉我们将Q_OBJECT个类放在标题中;那是因为HEADERS列出了标题,而不是来源。

      #3告诉我们确实将所有标题放入HEADERS列表中。这显然是因为如果包含Q_OBJECT的标头不在该列表中,则qmake将找不到它并发出规则。 (虽然对于不包含QObject子类的标头不是绝对必要的,但最好将每个标头放在那里以便不忘记。)

      #4告诉我们每次添加qmake或修改Q_OBJECT文件时重新运行.pro。规则的第一部分的原因是,如果qmake已经扫描了标题并且没有找到Q_OBJECT,那么它不会在Makefile中发出任何规则。但是添加Q_OBJECT也需要这些规则;因此我们需要qmake重新扫描标题,这正是重新运行qmake所做的事情。

      同样的原因适用于.pro被修改时(例如,添加更多标题时 - 可能Q_OBJECT下有HEADERS)。

      请注意,如果您使用类似GNU的make,则qmake会发出一条特殊规则,告知make重新运行qmake如果在Makefile之后修改.pro,则重新启动Makefile。这就是为什么通常在UNIX上修改qmake时不需要手动重新运行.pro - 只需运行make也会再次运行qmake。但这不适用无处不在


      那么,在Q_OBJECT文件中包含.cpp的类定义是否不可能?

      不,这完全可能 ,但它需要使用某种未记录的qmake功能。诀窍是添加如下行:

      #include "foobar.moc"
      

      foobar.cpp文件的末尾,该文件包含一个带有Q_OBJECT的更多类定义。

      qmake会找到此特殊内容并为moc生成foobar.moc生成.cpp的规则,然后foobar.moc将其包含在内,然后与其一起编译。 (因此,没有额外的规则来编译{{1}}也不会链接结果。)

答案 1 :(得分:0)

  

编辑1:如果我从MSFSPlugin :: MSFSPluginImpl的declation注释掉Q_OBJECT,那么错误就会消失,但是我失去了信号功能。

是的,Q_OBJECT宏对于信号,插槽,属性等是必需的。

  

编辑2:我看到moc仅对头文件进行操作。这可能与我的QObject派生类被声明&amp;在.cpp文件中定义?

是和否。我会解释..

通常,它会在您编写时通过分离来解决,但也可以在您的类定义之后包含moc文件以使其工作,但您需要记住不要在其中放置多个以避免奇怪的后果

因此,在您的情况下,您可以为实现标头建立MSFSPlugin_p.hMSFSPluginImpl.h文件。

顺便说一下,让pimpl受到保护是一个坏主意。 pimpl习语是指私有实现,不受保护。