g ++ неопределенная ссылка на typeinfo
Вопрос
Я только что столкнулся со следующей ошибкой (и нашел решение в Интернете, но его нет в 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