Ошибки компоновщика GCC С++: Undefined ссылка на "vtable for XXX", Undefined ссылка на "ClassName:: ClassName()"
Я создаю проект на С++ на Ubuntu x64, используя Eclipse-CDT. Я в основном делаю привет мир и связываюсь с коммерческой библиотекой сторонних разработчиков.
Я включил файлы заголовков, связанные с их библиотеками, но у меня все еще возникают ошибки компоновщика. Существуют ли какие-то возможные проблемы, кроме очевидных (например, я на 99% уверен, что я привязываюсь к правильной библиотеке).
- Есть ли способ подтвердить, что статические библиотеки, с которыми я связываюсь, являются 64-битными?
- Есть ли способ подтвердить, что библиотека имеет класс (и методы), который я ожидаю от него?
Eclipse говорит:
Building target: LinkProblem
Invoking: GCC C++ Linker
g++ -L/home/notroot/workspace/somelib-3/somelib/target/bin -o"LinkProblem" ./src/LinkProblem.o -lsomelib1 -lpthread -lsomelib2 -lsomelib3
./src/LinkProblem.o: In function `main':
/home/notroot/workspace/LinkProblem/Debug/../src/LinkProblem.cpp:17: undefined reference to `SomeClass::close()'
./src/LinkProblem.o: In function `SomeOtherClass':
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:148: undefined reference to `SomeClass::SomeClass()'
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:148: undefined reference to `vtable for SomeOtherClass'
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:151: undefined reference to `SomeClass::~SomeClass()'
./src/LinkProblem.o: In function `~SomeOtherClass':
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:140: undefined reference to `vtable for SomeOtherClass'
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:140: undefined reference to `SomeClass::~SomeClass()'
/home/notroot/workspace/somelib-3/somelib/include/sql/somefile.h:140: undefined reference to `SomeClass::~SomeClass()'
collect2: ld returned 1 exit status
make: *** [LinkProblem] Error 1
Ответы
Ответ 1
Предполагая, что эти методы находятся в одной из библиотек, это выглядит как проблема упорядочения.
При связывании библиотек с исполняемым файлом они выполняются в том порядке, в котором они объявлены.
Кроме того, компоновщик будет использовать только методы/функции, необходимые для разрешения в настоящее время незавершенных зависимостей. Если последующая библиотека использует методы/функции, изначально не требуемые объектами, у вас будут отсутствовать зависимости.
Как это работает:
- Возьмите все объектные файлы и объедините их в исполняемый файл
- Разрешить любые зависимости между объектными файлами.
- Для каждой библиотеки в порядке:
- Проверьте неразрешенные зависимости и посмотрите, разрешает ли их lib.
- Если требуется загрузить требуемую часть в исполняемый файл.
Пример:
Объекты требуют:
- Открыть
- Закрыть
- BatchRead
- BatchWrite
Lib 1 обеспечивает:
- Открыть
- Закрыть
- прочитать
- записи
Lib 2 предоставляет
- BatchRead (но использует lib1: read)
- BatchWrite (но использует lib1: write)
Если это связано так:
gcc -o plop plop.o -l1 -l2
Тогда компоновщик не сможет разрешить символы чтения и записи.
Но если я привяжу приложение следующим образом:
gcc -o plop plop.o -l2 -l1
Затем он будет правильно связываться. Поскольку l2 разрешает зависимости BatchRead и BatchWrite, но также добавляет два новых (чтение и запись). Когда мы связываемся с l1, все четыре зависимостей разрешаются.
Ответ 2
Эта ошибка компоновщика обычно (по моему опыту) означает, что вы переопределили виртуальную функцию в дочернем классе с объявлением, но не дали определения для метода. Например:
class Base
{
virtual void f() = 0;
}
class Derived : public Base
{
void f();
}
Но вы не дали определения f. Когда вы используете класс, вы получаете ошибку компоновщика. Как обычная ошибка компоновщика, это потому, что компилятор знал, о чем вы говорили, но линкер не смог найти определение. Это было очень сложно понять.
Ответ 3
Qt С++ покажет эту ошибку при изменении класса, который теперь наследуется от QObject (т.е. теперь он может использовать сигналы/слоты). Запуск qmake -r вызовет moc и устранит эту проблему.
Если вы работаете с другими с помощью какого-то контроля версий, вы захотите внести некоторые изменения в ваш .pro файл (т.е. добавить/удалить пустую строку). Когда все остальные получат ваши изменения и сделают make, make увидит, что файл .pro изменился и автоматически запустил qmake. Это спасет ваших товарищей по команде от повторения вашего разочарования.
Ответ 4
Проблема для меня оказалась довольно неясной. Мой класс выглядел так:
//-----------------------------------------
// libbase.h
class base {
public:
base() { }
virtual ~base() { }
virtual int foo() { return 0; }
}
//-----------------------------------------
//-----------------------------------------
// libbase.cpp
#include "libbase.h"
//-----------------------------------------
//-----------------------------------------
// main.h
class derived : public base {
public:
virtual int foo() ;
}
//-----------------------------------------
//-----------------------------------------
// main.cpp
int main () {
derived d;
}
//-----------------------------------------
Проблема заключается в компоновщике. Мой заголовочный файл куда-то попадал в библиотеку, но все виртуальные функции были объявлены "inline" в объявлении класса. Поскольку не было никакого кода, использующего виртуальные функции (пока), компилятор или компоновщик пренебрегли тем, чтобы нанести фактические тела функций на место. Он также не смог создать таблицу vtable.
В моем основном коде, где я получил из этого класса, компоновщик попытался подключить мой класс к базовому классу и его vtable. Но vtable был отброшен.
Решение заключалось в объявлении хотя бы одного из тел виртуальных функций вне объявления класса, например:
//-----------------------------------------
// libbase.h
class base {
public:
base() { }
virtual ~base() ; //-- No longer declared 'inline'
virtual int foo() { return 0; }
}
//-----------------------------------------
//-----------------------------------------
// libbase.cpp
#include "libbase.h"
base::~base()
{
}
//-----------------------------------------
Ответ 5
Что касается проблем с Qt4, я не мог использовать опцию qmake moc, упомянутую выше. Но в любом случае это не проблема. В определении класса у меня был следующий код:
class ScreenWidget : public QGLWidget
{
Q_OBJECT // must include this if you use Qt signals/slots
...
};
Мне пришлось удалить строку "Q_OBJECT", потому что у меня не было никаких сигналов или слотов.
Ответ 6
У меня появилось это сообщение об ошибке. Проблема заключалась в том, что я объявил виртуальный деструктор в файле заголовка, но тело виртуальных функций фактически не было реализовано.
Ответ 7
Эта ошибка также возникает, когда мы просто объявляем виртуальную функцию без определения в базовом классе.
Например:
class Base
{
virtual void method1(); // throws undefined reference error.
}
Измените приведенное выше объявление ниже, оно будет работать нормально.
class Base
{
virtual void method1()
{
}
}
Ответ 8
В моем случае проблема возникла, когда я забыл добавить = 0 для одной функции в моем чистом виртуальном классе. Он был исправлен при добавлении = 0. То же, что и для Фрэнка выше.
class ISettings
{
public:
virtual ~ISettings() {};
virtual void OKFunction() =0;
virtual void ProblemFunction(); // missing =0
};
class Settings : ISettings
{
virtual ~Settings() {};
void OKFunction();
void ProblemFunction();
};
void Settings::OKFunction()
{
//stuff
}
void Settings::ProblemFunction()
{
//stuff
}
Ответ 9
Я тоже наткнулся на этот вопрос. Приложение определило класс чистого виртуального интерфейса, и пользовательский класс, предоставляемый через общую библиотеку, должен был реализовать интерфейс. При связывании приложения компоновщик жаловался, что общая библиотека не будет предоставлять vtable и type_info для базового класса, а также не может быть найдена где-либо еще.
Оказалось, что я просто забыл сделать один из методов интерфейса чистым виртуальным (т.е. Опустить "= 0" в конце объявления. Очень рудиментарно, все еще легко упускать из виду и озадачивать, если вы не можете подключить диагностику компоновщика к первопричина.
Ответ 10
У меня было это сообщение об ошибке при попытке "привет мир", как вещи с Qt. Проблемы ушли, правильно выполнив qt moc (метаобъект метаобъекта) и скомпилировав +, включая эти файлы, созданные с помощью moc.
Ответ 11
Если у вас есть базовый класс с чистой виртуальной функцией, убедитесь, что у вашего конструктора базового класса и деструктора есть тело, иначе линкер не работает.
Ответ 12
Я помещаю это для будущих посетителей:
если вы получаете ошибку при создании объекта Exception
, то причиной этого может быть отсутствие определения для виртуальной функции what()
.