这一切都有很多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文件中定义?
答案 0 :(得分:33)
假设我们正在处理qmake
。
Q_OBJECT
派生类的定义中都存在QObject
宏。 QObject
- 派生类。 HEADERS=
列表的.pro文件中。 qmake
添加到其中一个班级或修改Q_OBJECT
文件时,请运行.pro
。编辑1:如果我从MSFSPlugin :: MSFSPluginImpl的declation注释掉Q_OBJECT,那么错误就会消失,但是我失去了信号功能。
烨。声明信号,广告位,可调用内容,Q_OBJECT
,翻译,属性,枚举和方法内省等需要 qobject_cast
。
#1黄金法则来自于没有 Q_OBJECT
你无法在课堂上使用qobject_cast
这样的内容;如果您使用(甚至间接)内省设施,例如debug an object hierarchy或dump all active connections to an object,那么您的类的对象将显示真实的类名(而不是超类的一个);等子>
Q_OBJECT
做了两件事:
moc
的调用,其作用是为类生成一些额外的代码。此代码将提供上面列出的所有设施; qt_metacall()
和metaObject()
。 moc
将为这些虚拟机生成实现。 您获得的错误是声明虚拟的典型症状(因为宏已在您的代码中展开)但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.h
或MSFSPluginImpl.h
文件。
顺便说一下,让pimpl受到保护是一个坏主意。 pimpl习语是指私有实现,不受保护。