Ошибки компоновщика 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().