簡體   English   中英

使用CONFIG + = staticlib構建Qt應用程序會導致“未定義引用vtable”錯誤

[英]Building Qt app with CONFIG += staticlib causes “undefined reference to vtable” errors

編輯:我已經大量編輯了這篇文章,將項目剝離到其基本要素。 我還添加了一個Github存儲庫 ,包括本文中未引用的文件。


我有一個使用一個Qt Creator的項目(QMAKE,Qt的5.2.0,3.0.0造物主) subdirs模板。 有三個子項目:

  1. Stadium - 配置為TEMPLATE = libCONFIG += staticlib
  2. Football - 配置為TEMPLATE = libCONFIG += staticlib並使用Field庫的庫。
  3. 服務器 - 使用體育場和足球庫的QML應用程序。

我正在Windows 8.1(MSVC2012)和Linux(gcc 4.8.1)上構建此應用程序。 它在Windows上沒有問題 ,但Linux構建表現奇怪。

我得到的錯誤看起來像這樣:

undefined reference to 'vtable for Stadium::Engine'

我已經將這個項目重寫為一組顯示錯誤的裸文件。 你可以在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

這是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行(以及相應的else:unix: PRE_TARGETDEPS += $$OUT_PWD/../stadium/libstadium.a在Game.pro太行,當然)。 這可以成功構建項目並且無問題地運行。 但我只是不明白為什么。 我也不明白為什么定義構造函數定義很重要。

有任何想法嗎?

答案非常簡單:圖書館鏈接的順序錯誤。

我查看了調用鏈接器的命令(鏈接器錯誤正上方):

g++ [...] -lStadium [...] -lFootball 

我還查看了代碼: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

現在命令行看起來像:

g++ [...] -lFootball [...] -lStadium

它在我的Linux機器上編譯並運行得很好。

您已內聯虛擬析構函數。
這有時會導致問題。
嘗試在.cpp文件中實現析構函數。 我也會從析構函數的聲明中刪除= 0

你可以看看其他問題:

我嘗試將您的STADIUM_ENGINE代碼編譯為靜態庫,然后從應用程序進行鏈接,並且在沒有定義虛擬純析構函數時(如預期的那樣)我得到了錯誤。 如果未定義虛擬析構函數,則無法實例化任何派生類。

無論如何,你的類繼承自QObject,它已經聲明並實現了一個非純虛析構函數。 純虛擬析構函數是否有用?

好的,我找到了解決方案。 我有三個不同的問題,當更改時,清除了vtable錯誤。 不幸的是,我不知道為什么后兩個變化是必要的。

1.派生類中的Q_OBJECT

繼承上面的Stadium :: Engine類的類里面有一個額外的Q_OBJECT。 當我在派生類中刪除第二個Q_OBJECT時,其中一個vtable錯誤就消失了。

2. Engine構造函數

我不明白為什么,但是當派生類在CPP文件中定義構造vtable ,它會給出vtable錯誤。 當在標題中定義時(在類描述中),它可以正常工作。 構造函數中沒有任何內容( DerivedEngine() {} )。 我無法弄清楚為什么這很重要。

3.定義構造函數和虛擬析構函數

要求構造函數和析構函數都在純抽象類中定義。 我不理解為什么。 我在類定義之外的標題中添加了這些行:

inline Stadium::Engine::Engine() {}
inline Stadium::Engine::~Engine() {}

這仍然困擾着我。 為什么這些變化是必要的? 為什么這只發生在gcc / Linux上? 當然這是一個Qt bug,對吧?

我沒看到你在哪里運行moc編譯器。 moc創建一個文件,為您的QObject派生類解析其他內容。

如果moc正在運行,您的命名空間可能就是問題所在。 眾所周知,moc不能很好地與命名空間一起使用。 我喜歡命名空間,但Qt在人們開始在各地使用之前就已存在。

如果該類不是絕對必需的,那么從QObject中刪除Q_OBJECT和派生是另一種解決方案。

另一種可能性是你的makefile過時了。 在這種情況下,您需要強制運行qmake以確保它們正確刷新。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM