我有一个使用subdirs
模板的Qt Creator项目(qmake,Qt 5.2.0,Creator 3.0.0)。有三个子项目:
TEMPLATE = lib
和CONFIG += staticlib
的图书馆。TEMPLATE = lib
和CONFIG += staticlib
且使用Field
库的库。我正在Windows 8.1(MSVC2012)和Linux(gcc 4.8.1)上构建此应用程序。 在Windows上没有问题,但Linux版本的行为很奇怪。
我得到的错误如下:
undefined reference to 'vtable for Stadium::Engine'
我已将此项目重写为一组显示错误的裸文件。你可以在Github上找到它:Football。随意克隆它,并为自己看到所有的错误。 661441c
提交解决了问题,09836f9
提交包含错误。
Stadium Engine.h文件是一个抽象类。它看起来像这样:
#ifndef STADIUM_ENGINE_H
#define STADIUM_ENGINE_H
#include <QObject>
namespace Stadium {
class Engine : public QObject
{
Q_OBJECT
public slots:
virtual void executeCommand() = 0;
};
} // namespace Stadium
#endif // STADIUM_ENGINE_H
这是Football Engine.h文件,它继承自上面的Stadium Engine.h文件:
#ifndef FOOTBALL_ENGINE_H
#define FOOTBALL_ENGINE_H
#include <QObject>
#include "../Stadium/Engine.h"
namespace Football
{
class Engine : public Stadium::Engine
{
Q_OBJECT
public:
Engine();
~Engine() {}
public slots:
void executeCommand();
};
} // namespace Football
#endif // FOOTBALL_ENGINE_H
还有Football Engine.cpp文件:
#include "Engine.h"
#include <QDebug>
Football::Engine::Engine()
{
qDebug() << "[Football::Engine] Created.";
}
void Football::Engine::executeCommand()
{
qDebug() << "[Football::Engine] The command was executed.";
}
如果我将构造函数定义从cpp移动到头文件,则构建时没有错误。
以下是Server.pro文件。它表示我所有其他专业文件,因为静态链接描述(由Qt Creator自动生成)看起来是一样的。
QT += core
QT -= gui
TARGET = Server
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../Stadium/release/ -lStadium
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../Stadium/debug/ -lStadium
else:unix: LIBS += -L$$OUT_PWD/../Stadium/ -lStadium
INCLUDEPATH += $$PWD/../Stadium
DEPENDPATH += $$PWD/../Stadium
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Stadium/release/libStadium.a
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Stadium/debug/libStadium.a
else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Stadium/release/Stadium.lib
else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Stadium/debug/Stadium.lib
else:unix: PRE_TARGETDEPS += $$OUT_PWD/../Stadium/libStadium.a
win32:CONFIG(release, debug|release): LIBS += -L$$OUT_PWD/../Football/release/ -lFootball
else:win32:CONFIG(debug, debug|release): LIBS += -L$$OUT_PWD/../Football/debug/ -lFootball
else:unix: LIBS += -L$$OUT_PWD/../Football/ -lFootball
INCLUDEPATH += $$PWD/../Football
DEPENDPATH += $$PWD/../Football
win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Football/release/libFootball.a
else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Football/debug/libFootball.a
else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Football/release/Football.lib
else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$OUT_PWD/../Football/debug/Football.lib
else:unix: PRE_TARGETDEPS += $$OUT_PWD/../Football/libFootball.a
我尝试过清理,重新运行qmake,删除构建目录以及重建。在Linux中构建此项目的唯一方法是删除Stadium库的.pro文件中的CONFIG += staticlib
行(当然也可以删除Game.pro中相应的else:unix: PRE_TARGETDEPS += $$OUT_PWD/../stadium/libstadium.a
行)。这可以成功构建项目并且无问题地运行。但我只是不明白为什么。我也不明白为什么定义构造函数定义很重要。
有什么想法吗?
答案 0 :(得分:13)
答案非常简单:图书馆的链接顺序错误。
我查看了调用链接器的命令(链接器错误正上方):
g++ [...] -lStadium [...] -lFootball
我还查看了代码:Football子项目引用了Stadium子项目,因此库的顺序错误,例如请参阅GCC C++ Linker errors: Undefined reference to 'vtable for XXX', Undefined reference to 'ClassName::ClassName()'的接受答案进行解释。
实际上,如果我交换Server.pro文件中的两个库(派生自提交09836f9
,为了简洁起见,删除了无关的win32特定细节):
[...]
SOURCES += main.cpp
LIBS += -L$$OUT_PWD/../Football/ -lFootball
INCLUDEPATH += $$PWD/../Football
DEPENDPATH += $$PWD/../Football
PRE_TARGETDEPS += $$OUT_PWD/../Football/libFootball.a
LIBS += -L$$OUT_PWD/../Stadium/ -lStadium
INCLUDEPATH += $$PWD/../Stadium
DEPENDPATH += $$PWD/../Stadium
PRE_TARGETDEPS += $$OUT_PWD/../Stadium/libStadium.a
现在命令行看起来像:
g++ [...] -lFootball [...] -lStadium
它在我的Linux机器上编译并运行得很好。
答案 1 :(得分:0)
您已经内联了虚拟析构函数
这有时会导致问题
尝试在.cpp文件中实现析构函数。我也会从析构函数的声明中删除= 0
。
答案 2 :(得分:0)
你可以看看其他问题:
我尝试将您的STADIUM_ENGINE代码编译为静态库然后从应用程序进行链接,并且在没有定义虚拟纯析构函数时(如预期的那样)我得到了错误。如果您没有定义虚拟析构函数,则无法实例化任何派生类。
无论如何,你的类继承自QObject,它已经声明并实现了一个非纯虚析构函数。纯虚拟析构函数是否有用?
答案 3 :(得分:0)
好的,我找到了解决方案。我有三个不同的问题,当更改时,清除了vtable错误。不幸的是,我不知道为什么后两个变化是必要的。
继承上面的Stadium :: Engine类的类里面有一个额外的Q_OBJECT。当我在派生类中删除第二个Q_OBJECT时,其中一个vtable错误就消失了。
我不明白为什么,但是当派生类在CPP文件中定义构造函数时,它会给出vtable
错误。当在标题中定义时(在类描述中),它可以正常工作。构造函数中没有任何内容(DerivedEngine() {}
)。我无法弄清楚为什么这很重要。
要求构造函数和析构函数都在纯抽象类中定义。我不懂为什么。我在标题中添加了这些行,在类定义之外:
inline Stadium::Engine::Engine() {}
inline Stadium::Engine::~Engine() {}
这仍然让我感到困扰。为什么这些变化是必要的?为什么仅与gcc / Linux一起发生?当然这是一个Qt bug,对吗?
答案 4 :(得分:-1)
我没看到你在哪里运行moc编译器。 moc创建一个文件,为QObject派生类解析其他内容。
如果moc正在运行,您的命名空间可能就是问题所在。众所周知,moc不能很好地与命名空间一起使用。我喜欢命名空间,但Qt在人们开始在任何地方使用它们之前就已存在。
从QObject中删除Q_OBJECT和派生是另一种解决方案,如果该类不是绝对必需的话。
另一种可能性是你的makefile过时了。在这种情况下,您需要强制运行qmake以确保它们正确刷新。