[英]Building Qt app with CONFIG += staticlib causes “undefined reference to vtable” errors
編輯:我已經大量編輯了這篇文章,將項目剝離到其基本要素。 我還添加了一個Github存儲庫 ,包括本文中未引用的文件。
我有一個使用一個Qt Creator的項目(QMAKE,Qt的5.2.0,3.0.0造物主) subdirs
模板。 有三個子項目:
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上找到它: 足球 。 隨意克隆它,並為自己看到所有的錯誤。 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錯誤。 不幸的是,我不知道為什么后兩個變化是必要的。
繼承上面的Stadium :: Engine類的類里面有一個額外的Q_OBJECT。 當我在派生類中刪除第二個Q_OBJECT時,其中一個vtable錯誤就消失了。
我不明白為什么,但是當派生類在CPP文件中定義構造vtable
,它會給出vtable
錯誤。 當在標題中定義時(在類描述中),它可以正常工作。 構造函數中沒有任何內容( DerivedEngine() {}
)。 我無法弄清楚為什么這很重要。
要求構造函數和析構函數都在純抽象類中定義。 我不理解為什么。 我在類定義之外的標題中添加了這些行:
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.