使用CONFIG + = staticlib构建Qt应用程序会导致"未定义引用vtable"错误

jmb*_*eck 15 c++ linux qt gcc qt-creator

编辑:我已经大量编辑了这篇文章,将项目剥离到其基本要素.我还添加了一个Github存储库,包括本文中未引用的文件.


我有一个使用subdirs模板的Qt Creator项目(qmake,Qt 5.2.0,Creator 3.0.0).有三个子项目:

  1. 体育场 - 配置为TEMPLATE = lib和的图书馆CONFIG += staticlib.
  2. 足球-被配置为图书馆TEMPLATE = libCONFIG += staticlib并使用Field图书馆.
  3. 服务器 - 使用体育场和足球库的QML应用程序.

我正在Windows 8.1(MSVC2012)和Linux(gcc 4.8.1)上构建此应用程序.它在Windows上没有问题,但Linux构建表现奇怪.

我得到的错误看起来像这样:

undefined reference to 'vtable for Stadium::Engine'
Run Code Online (Sandbox Code Playgroud)

我已经将这个项目重写为一组显示错误的裸文件.你可以在Github上找到它:足球.随意克隆它,并为自己看到所有的错误.该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
Run Code Online (Sandbox Code Playgroud)

这是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
Run Code Online (Sandbox Code Playgroud)

和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.";
}
Run Code Online (Sandbox Code Playgroud)

如果我将构造函数定义从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
Run Code Online (Sandbox Code Playgroud)

我已经尝试过清理,重新运行qmake,删除构建目录和重建.在Linux中构建此项目的唯一方法是删除CONFIG += staticlibStadium库的.pro文件中的else:unix: PRE_TARGETDEPS += $$OUT_PWD/../stadium/libstadium.a行(当然也包括Game.pro中的相应行).这样就可以成功构建项目并且无问题地运行.但我只是不明白为什么.我也不明白为什么定义构造函数定义很重要.

有任何想法吗?

Ali*_*Ali 13

答案非常简单:图书馆链接的顺序错误.

我查看了调用链接器的命令(链接器错误正上方):

g++ [...] -lStadium [...] -lFootball 
Run Code Online (Sandbox Code Playgroud)

我还查看了代码:Football子项目引用了Stadium子项目,因此库的顺序错误,例如,参见GCC C++ Linker错误的接受答案:未定义的引用'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
Run Code Online (Sandbox Code Playgroud)

现在命令行看起来像:

g++ [...] -lFootball [...] -lStadium
Run Code Online (Sandbox Code Playgroud)

它在我的Linux机器上编译并运行得很好.

  • 你在开玩笑吧.我无法相信gcc链接器如此简单.我用Java编程的时间太长了.我改变了原始项目中的顺序,并且有效.可悲的是,我故意按顺序添加库,因为它只是"将非依赖库放在第一位"是有意义的.啧.谢谢你的帮助.我今天学了些新东西. (2认同)