Вопрос

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

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

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

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

Это было полезно?

Решение

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

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

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

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

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

Линия

virtual void fn();

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

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

extern int i;
int *pi = &i;

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

Другие советы

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

[ source ]

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

virtual void foo();

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

virtual void foo() {}

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

virtual void foo() = 0;

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

  

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

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

  

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

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

Если вы связываете один .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. " Это усложняет работу компилятора и компоновщика, возможно, в ущерб производительности кода.

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

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

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

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

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

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

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

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

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

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

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

Подобно обсуждению 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 работать должным образом.

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

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

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

Я получил много этих ошибок только сейчас. Случилось так, что я разделил класс только для заголовочного файла на заголовочный файл и файл cpp. Однако я не обновил свою систему сборки, поэтому файл cpp не был скомпилирован. Среди просто неопределенных ссылок на функции, объявленные в заголовке, но не реализованные, я получил много этих ошибок typeinfo.

Решением было перезапустить систему сборки, чтобы скомпилировать и связать новый файл cpp.

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

как упомянул @sergiy, зная, что это может быть проблемой 'rtti', мне удалось обойти ее, поместив реализацию конструктора в отдельный файл .cpp и применив флаги компиляции -fno-rtti к файл . это работает хорошо.

, поскольку я до сих пор не совсем понимаю внутреннюю ошибку этой ссылки, я не уверен, является ли мое решение общим. однако, я думаю, что стоит попробовать, прежде чем пытаться использовать адаптер, как упомянуто @francois. и, конечно, если доступны все исходные коды (не в моем случае), лучше перекомпилируйте с помощью '-frtti', если это возможно.

Еще одна вещь, если вы решите попробовать мое решение, попробуйте сделать отдельный файл как можно более простым и не использовать некоторые изящные функции C ++. уделять особое внимание вещам, связанным с повышением, потому что многое зависит от RTT.

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

У меня было

class ICommProvider { public: /** * @brief If connection is established, it sends the message into the server. * @param[in] msg - message to be send * @return 0 if success, error otherwise */ virtual int vaSend(const std::string &msg) = 0; /** * @brief If connection is established, it is waiting will server response back. * @param[out] msg is the message received from server * @return 0 if success, error otherwise */ virtual int vaReceive(std::string &msg) = 0; virtual int vaSendRaw(const char *buff, int bufflen) = 0; virtual int vaReceiveRaw(char *buff, int bufflen) = 0; /** * @bief Closes current connection (if needed) after serving * @return 0 if success, error otherwise */ virtual int vaClose(); };

Последний vaClose не является виртуальным, поэтому скомпилированный не знал, где взять для него реализацию, и тем самым запутался.мое сообщение было таким:

...TcpClient.o:(.rodata+0x38):неопределенная ссылка на `typeinfo для ICommProvider'

Простое изменение с

virtual int vaClose();

Для

virtual int vaClose() = 0;

исправлена проблема.надеюсь, это поможет

Я сталкиваюсь с редкой ситуацией, но это может помочь другим друзьям в аналогичной ситуации. Я должен работать на старой системе с gcc 4.4.7. Я должен скомпилировать код с поддержкой c ++ 11 или выше, поэтому я собираю последнюю версию gcc 5.3.0. При сборке моего кода и создании ссылок на зависимости, если зависимость создается с помощью более старого компилятора, я получил ошибку «неопределенная ссылка на», хотя я четко определил путь ссылки с помощью -L / path / to / lib -llibname. Некоторые пакеты, такие как boost и проекты, собранные с помощью cmake, обычно имеют тенденцию использовать более старый компилятор, и они обычно вызывают такие проблемы. Вы должны пройти долгий путь, чтобы убедиться, что они используют более новый компилятор.

В моем случае это просто проблема зависимости от библиотеки, даже если у меня есть вызов dynamic_cast. После добавления достаточного количества зависимостей в make-файл эта проблема исчезла.

Убедитесь, что ваши зависимости были скомпилированы без -f-nortti .

Для некоторых проектов вы должны установить это явно, как в RocksDB:

USE_RTTI=1 make shared_lib -j4
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top