g ++ неопределенная ссылка на typeinfo - PullRequest
184 голосов
/ 21 ноября 2008

Я только что наткнулся на следующую ошибку (и нашел решение в сети, но его нет в переполнении стека):

(. Gnu.linkonce. [Stuff]): не определено ссылка на [метод] [объект файл] :( gnu.linkonce [материал])..: неопределенная ссылка на `typeinfo для [Имя_класс]

Почему можно получить одну из этих ошибок компоновщика "неопределенная ссылка на typeinfo"?

(Бонусные баллы, если вы можете объяснить, что происходит за кулисами.)

Ответы [ 16 ]

193 голосов
/ 21 ноября 2008

Одной из возможных причин является то, что вы объявляете виртуальную функцию, не определяя ее.

Когда вы объявляете это, не определяя его в том же модуле компиляции, вы указываете, что он определен где-то еще - это означает, что фаза компоновщика попытается найти его в одном из других модулей (или библиотек) компиляции.

Пример определения виртуальной функции:

virtual void fn() { /* insert code here */ }

В этом случае вы присоединяете определение к объявлению, что означает, что компоновщику не нужно разрешать его позже.

Линия

virtual void fn();

объявляет fn() без его определения и вызывает сообщение об ошибке, о котором вы спрашивали.

Это очень похоже на код:

extern int i;
int *pi = &i;

, в котором указано, что целое число i объявлено в другом модуле компиляции, который должен быть разрешен во время соединения (в противном случае pi не может быть установлен по его адресу).

133 голосов
/ 15 ноября 2010

Это также может произойти, когда вы смешиваете код -fno-rtti и -frtti. Затем необходимо убедиться, что для любого класса, к которому type_info осуществляется доступ в коде -frtti, метод ключа скомпилирован с -frtti. Такой доступ может произойти, когда вы создаете объект класса, используете dynamic_cast и т. Д.

[ 1010 * источник *]

42 голосов
/ 21 ноября 2008

Это происходит, когда в объявленных (не чистых) виртуальных функциях отсутствуют тела. В вашем определении класса, что-то вроде:

virtual void foo();

Должен быть определен (встроенный или в связанном исходном файле):

virtual void foo() {}

Или объявлено чисто виртуальным:

virtual void foo() = 0;
25 голосов
/ 21 ноября 2008

Цитирование из Руководство по gcc :

Для полиморфных классов (классов с виртуальными функциями) объект type_info записывается вместе с vtable [...] Для всех других типов мы выписываем объект type_info, когда он используется: когда применяется `typeid 'к выражение, выбрасывание объекта или ссылка на тип в предложении catch или спецификации исключения.

И чуть раньше на той же странице:

Если класс объявляет любые не встроенные, не чистые виртуальные функции, первая из них выбирается в качестве «ключевого метода» для класса, а виртуальная таблица генерируется только в модуле перевода, где определен ключевой метод.

Итак, эта ошибка возникает, когда «ключевой метод» не имеет определения, как уже упоминалось в других ответах.

18 голосов
/ 25 июня 2010

Если вы связываете один .so с другим, еще одной возможностью является компиляция с "-fvisibility = hidden" в gcc или g ++. Если оба файла .so были созданы с "-fvisibility = hidden" и метод ключа не совпадает с .so, как другая реализация виртуальной функции, последний не увидит vtable или typeinfo первого. Для компоновщика это выглядит как нереализованная виртуальная функция (как в ответах paxdiablo и cdleary).

В этом случае вы должны сделать исключение для видимости базового класса с помощью

__attribute__ ((visibility("default")))

в объявлении класса. Например,

class __attribute__ ((visibility("default"))) boom{
    virtual void stick();
}

Другое решение, конечно, состоит в том, чтобы не использовать «-fvisibility = hidden». Это усложняет работу компилятора и компоновщика, возможно, в ущерб производительности кода.

14 голосов
/ 21 ноября 2008

Предыдущие ответы верны, но эта ошибка также может быть вызвана попыткой использовать typeid для объекта класса, который имеет нет виртуальных функций. C ++ RTTI требует vtable, поэтому классы, для которых вы хотите выполнить идентификацию типа, требуют как минимум одну виртуальную функцию.

Если вы хотите, чтобы информация о типах работала с классом, для которого вы на самом деле не хотите никаких виртуальных функций, сделайте деструктор виртуальным.

9 голосов
/ 11 июня 2011

Возможные решения для кода, работающего с библиотеками RTTI и не-RTTI:

a) Перекомпилируйте все с помощью -frtti или -fno-rtti
б) если а) для вас это невозможно, попробуйте следующее:

Предположим, что libfoo собран без RTTI. Ваш код использует libfoo и компилируется с RTTI. Если вы используете класс (Foo) в libfoo с виртуальными объектами, вы, скорее всего, столкнетесь с ошибкой во время компоновки, которая говорит: отсутствует typeinfo для класса Foo.

Определите другой класс (например, FooAdapter), который не имеет виртуальных и будет перенаправлять вызовы в Foo, который вы используете.

Компилировать FooAdapter в небольшой статической библиотеке, которая не использует RTTI и зависит только от символов libfoo. Обеспечьте заголовок для этого и используйте это вместо этого в своем коде (который использует RTTI). Поскольку FooAdapter не имеет виртуальной функции, у него не будет никакой информации о типе, и вы сможете связать свой двоичный файл. Если вы используете много разных классов из libfoo, это решение может быть не удобным, но это только начало.

8 голосов
/ 02 декабря 2015

Я просто потратил несколько часов на эту ошибку, и хотя другие ответы здесь помогли мне понять, что происходит, они не решили мою конкретную проблему.

Я работаю над проектом, который компилируется с использованием clang++ и g++. У меня не было проблем со связыванием при использовании clang++, но я получал ошибку undefined reference to 'typeinfo for с g++.

Важный момент: Связывание порядка МАТЕРИИ с g++. Если вы перечислите библиотеки, которые хотите связать, в неправильном порядке, вы можете получить ошибку typeinfo.

См. этот вопрос SO для получения дополнительной информации о порядке связывания с gcc / g++.

6 голосов
/ 14 мая 2014

Подобно обсуждению RTTI, NO-RTTI выше, эта проблема также может возникать, если вы используете dynamic_cast и не можете включить объектный код, содержащий реализацию класса.

Я столкнулся с этой проблемой при сборке Cygwin, а затем портировал код на Linux. Файлы make, структура каталогов и даже версии gcc (4.8.2) были идентичны в обоих случаях, но код связывался и работал правильно на Cygwin, но не мог связать на Linux. Red Hat Cygwin, по-видимому, произвёл модификации компилятора / компоновщика, чтобы избежать требования связывания объектного кода.

Сообщение об ошибке компоновщика Linux правильно направило меня к строке dynamic_cast, но более ранние сообщения на этом форуме заставляли меня искать пропущенные реализации функций, а не реальную проблему: отсутствующий объектный код. Мой обходной путь состоял в том, чтобы заменить функцию виртуального типа в базовом и производном классе, например Виртуальный int isSpecialType (), а не использовать dynamic_cast. Этот метод позволяет избежать необходимости связывать код реализации объекта только для того, чтобы заставить dynamic_cast работать должным образом.

5 голосов
/ 20 апреля 2011

В базовом классе (абстрактный базовый класс) вы объявляете виртуальный деструктор и, поскольку вы не можете объявить деструктор как чисто виртуальную функцию, вы должны определить его прямо здесь, в абстрактном классе, просто фиктивное определение, подобное виртуальному ~ base () {} подойдет или в любом из производных классов.

Если вы не сделаете этого, вы получите «неопределенный символ» во время ссылки. Поскольку VMT имеет запись для всех чисто виртуальных функций с совпадающим NULL, он обновляет таблицу в зависимости от реализации в производном классе. Но для не чистых, но виртуальных функций, ему нужно определение во время соединения, чтобы можно было обновить таблицу VMT.

Используйте C ++ Filta, чтобы разобрать символ. Мне нравится $ c ++ Filter _ZTIN10storageapi8BaseHostE выведет что-то вроде «typeinfo для storageapi :: BaseHost».

...