Создание приложения Qt с помощью CONFIG + = staticlib вызывает ошибки undefined для ссылок на vtable "
EDIT: я сильно отредактировал этот пост, чтобы лишить проект до его сути. Я также добавил репозиторий Github, в том числе файлы, на которые не ссылаются в этом сообщении.
У меня есть проект Qt Creator (qmake, Qt 5.2.0, Creator 3.0.0), который использует шаблон subdirs
. Существует три подпроекта:
- Стадион - библиотека, настроенная как
TEMPLATE = lib
и CONFIG += staticlib
.
- Футбол - библиотека, которая настроена как
TEMPLATE = lib
и CONFIG += staticlib
и использует библиотеку Field
.
- Сервер - приложение QML, которое использует библиотеки как в Стадион, так и в футболе.
Я создаю это приложение как для 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. Это указывает на все мои другие файлы 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 - это удалить строку CONFIG += staticlib
в .pro файле библиотеки стадиона (и соответствующую строку else:unix: PRE_TARGETDEPS += $$OUT_PWD/../stadium/libstadium.a
в Game.pro тоже, конечно). Это успешно создает проект и запускается без проблем. Но я просто не понимаю, почему. Я также не понимаю, почему это имеет значение, когда определено определение конструктора.
Любые идеи?
Ответы
Ответ 1
Ответ неутешительно прост: библиотеки были связаны в неправильном порядке.
Я посмотрел на команду, которая вызывала компоновщик (прямо над ошибкой компоновщика):
g++ [...] -lStadium [...] -lFootball
Я также просмотрел код: подпроект Football относится к подпроекту стадиона, поэтому библиотеки находятся в неправильном порядке, см., например, принятый ответ ошибки GCC С++ Linker: Undefined ссылка на "vtable для XXX" , Undefined ссылка на "ClassName:: ClassName()" для объяснения.
В самом деле, если я поменяю две библиотеки в файле Server.pro(производные от commit 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.
Ответ 2
Вы ввели виртуальный деструктор.
Иногда это может привести к проблемам.
Попробуйте реализовать деструктор в файле .cpp. Я также удаляю = 0
из объявления деструктора.
Ответ 3
Вы можете взглянуть на другие вопросы:
Я попытался скомпилировать ваш код STADIUM_ENGINE как статический lib, а затем связать его с приложением, и я только что получил ошибку, когда NO виртуальный чистый деструктор определен (как и ожидалось). Если у вас нет определенного виртуального деструктора, вы не сможете создать экземпляр какого-либо производного класса.
Во всяком случае, ваш класс наследует от QObject, который уже объявляет и реализует не чистый виртуальный деструктор. Полезен ли это чистый виртуальный деструктор?
Ответ 4
Хорошо, я понял решение. У меня было три различных вопроса, которые при изменении изменили ошибки 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, верно?
Ответ 5
Я не вижу, где вы запускаете компилятор moc. moc создает файл, который разрешает дополнительные вещи для ваших производных классов QObject.
Если moc запущен, может возникнуть проблема с вашим пространством имен. moc, как известно, хорошо работает с пространствами имен. Мне нравятся пространства имен, но Qt находится там, прежде чем люди начали использовать их везде.
Удаление Q_OBJECT и вывод из QObject - это другое решение, если это не совсем необходимо для этого класса.
Другая возможность заключается в том, что ваши make файлы устарели. В этом случае вы хотите принудительно запустить qmake, чтобы убедиться, что они правильно обновлены.