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

时间:2014-02-02 15:03:48

标签: c++ linux qt gcc qt-creator

编辑:我已经大量编辑了这篇文章,将项目剥离到了基本要素。我还添加了Github repository,包括此帖子中未引用的文件。


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

  1. 体育场 - 配置为TEMPLATE = libCONFIG += staticlib的图书馆。
  2. 足球 - 配置为TEMPLATE = libCONFIG += staticlib且使用Field库的库。
  3. 服务器 - 使用体育场和足球库的QML应用程序。
  4. 我正在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行)。这可以成功构建项目并且无问题地运行。但我只是不明白为什么。我也不明白为什么定义构造函数定义很重要。

    有什么想法吗?

5 个答案:

答案 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错误。不幸的是,我不知道为什么后两个变化是必要的。

1。派生类中的Q_OBJECT

继承上面的Stadium :: Engine类的类里面有一个额外的Q_OBJECT。当我在派生类中删除第二个Q_OBJECT时,其中一个vtable错误就消失了。

2。引擎构造函数

我不明白为什么,但是当派生类在CPP文件中定义构造函数时,它会给出vtable错误。当在标题中定义时(在类描述中),它可以正常工作。构造函数中没有任何内容(DerivedEngine() {})。我无法弄清楚为什么这很重要。

3。定义构造函数和虚拟析构函数

要求构造函数和析构函数都在纯抽象类中定义。我不懂为什么。我在标题中添加了这些行,在类定义之外:

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以确保它们正确刷新。