Я просто наткнулся на следующую ошибку (и нашел решение в сети, но его нет в переполнении стека):
(.gnu.linkonce. [stuff]): неопределенная ссылка на [метод] [объектный файл] :(. gnu.linkonce. [stuff]): неопределенная ссылка на `typeinfo для [classname] '
Почему можно получить одну из этих ошибок компоновщика «неопределенная ссылка на typeinfo»?
(Бонусные баллы, если вы можете объяснить, что происходит за кулисами.)
virtual void abc() =0;
( как если бы базовая версия никогда не называлась)abc()
таким образом, вы можете легко забыть переопределитьabc()
в производном классе и подумать, что все в порядке, так как вы все равно можете вызывать функцию без проблем. Хорошая практика для реализации чисто виртуальных функций найдена в этой статье , и она состоит в том, чтобы заставить функцию печатать «Вызов чисто виртуальной функции», а затем завершить работу программы.= 0;
.Ответы:
Одна из возможных причин заключается в том, что вы объявляете виртуальную функцию, не определяя ее.
Когда вы объявляете это, не определяя его в том же модуле компиляции, вы указываете, что он определен где-то еще - это означает, что фаза компоновщика будет пытаться найти его в одном из других модулей компиляции (или библиотек).
Пример определения виртуальной функции:
В этом случае вы присоединяете определение к объявлению, что означает, что компоновщику не нужно разрешать его позже.
Линия
объявляет
fn()
без определения и вызовет сообщение об ошибке, о котором вы спрашивали.Это очень похоже на код:
в котором говорится, что целое число
i
объявлено в другом модуле компиляции, который должен быть разрешен во время соединения (иначеpi
нельзя установить его адрес).источник
virtual void fn() = 0
это определение. Это не определение, а просто декларация . Единственная причина, по которой компоновщик не пытается ее разрешить, заключается в том, что соответствующая запись VMT не будет ссылаться на тело функции (скорее всего, будет содержать нулевой указатель). Однако никто не запрещает вам вызывать эту чисто виртуальную функцию не виртуальным способом, то есть с использованием полностью определенного имени. В этом случае компоновщик будет искать тело, и вам нужно будет определить функцию. И да, вы можете определить тело для чисто виртуальной функции.Это также может произойти, когда вы смешиваете
-fno-rtti
и-frtti
кодируете. Затем необходимо убедиться, что для любого класса, к которомуtype_info
осуществляется доступ в-frtti
коде, скомпилирован метод key-frtti
. Такой доступ может произойти, когда вы создаете объект класса, используетеdynamic_cast
и т. Д.[ источник ]
источник
Это происходит, когда в объявленных (не чистых) виртуальных функциях отсутствуют тела. В вашем определении класса, что-то вроде:
Должен быть определен (встроенный или в связанном исходном файле):
Или объявлен чисто виртуальным:
источник
Цитирование из руководства gcc :
И чуть раньше на той же странице:
Таким образом, эта ошибка возникает, когда «ключевой метод» не имеет определения, как уже упоминалось в других ответах.
источник
Если вы связываете один .so с другим, еще одной возможностью является компиляция с "-fvisibility = hidden" в gcc или g ++. Если оба файла .so были созданы с "-fvisibility = hidden" и метод ключа не совпадает с .so, как другая реализация виртуальной функции, последний не увидит vtable или typeinfo первого. Для линкера это выглядит как нереализованная виртуальная функция (как в ответах paxdiablo и cdleary).
В этом случае вы должны сделать исключение для видимости базового класса с
в объявлении класса. Например,
Другое решение, конечно, состоит в том, чтобы не использовать «-fvisibility = hidden». Это усложняет работу компилятора и компоновщика, возможно, в ущерб производительности кода.
источник
Предыдущие ответы верны, но эта ошибка также может быть вызвана попыткой использовать typeid для объекта класса, который не имеет виртуальных функций. C ++ RTTI требует vtable, поэтому классы, для которых вы хотите выполнить идентификацию типа, требуют как минимум одну виртуальную функцию.
Если вы хотите, чтобы информация о типах работала с классом, для которого вы на самом деле не хотите никаких виртуальных функций, сделайте деструктор виртуальным.
источник
Я просто потратил несколько часов на эту ошибку, и хотя другие ответы здесь помогли мне понять, что происходит, они не решили мою конкретную проблему.
Я работаю над проектом, который компилируется с использованием обоих
clang++
иg++
. У меня не было проблем с линковкойclang++
, но я получал сообщение обundefined reference to 'typeinfo for
ошибкеg++
.Важный момент: связывание порядка вопросов с
g++
. Если вы перечислите библиотеки, которые хотите связать, в неправильном порядке, вы можете получитьtypeinfo
ошибку.Смотрите этот вопрос SO для более подробной информации о связывании порядка с
gcc
/g++
.источник
Возможные решения для кода, которые работают с библиотеками RTTI и не-RTTI:
a) Перекомпилируйте все с помощью -frtti или -fno-rtti
b) Если a) для вас невозможно, попробуйте следующее:
Предположим, что libfoo собран без RTTI. Ваш код использует libfoo и компилируется с RTTI. Если вы используете класс (Foo) в libfoo, который имеет виртуалы, вы, скорее всего, столкнетесь с ошибкой во время компоновки, которая говорит: отсутствует typeinfo для класса Foo.
Определите другой класс (например, FooAdapter), который не имеет виртуальных и будет перенаправлять вызовы в Foo, который вы используете.
Скомпилируйте FooAdapter в небольшой статической библиотеке, которая не использует RTTI и зависит только от символов libfoo. Обеспечьте заголовок для этого и используйте это вместо этого в своем коде (который использует RTTI). Поскольку FooAdapter не имеет виртуальной функции, у него не будет никакой информации о типе, и вы сможете связать свой двоичный файл. Если вы используете много разных классов из libfoo, это решение может быть не удобным, но это только начало.
источник
Подобно обсуждению RTTI, NO-RTTI выше, эта проблема также может возникнуть, если вы используете dynamic_cast и не можете включить объектный код, содержащий реализацию класса.
Я столкнулся с этой проблемой, основываясь на Cygwin, а затем портировал код на Linux. Файлы make, структура каталогов и даже версии gcc (4.8.2) были идентичны в обоих случаях, но код связывался и работал правильно на Cygwin, но не мог связать на Linux. Red Hat Cygwin, по-видимому, сделал модификации компилятора / компоновщика, чтобы избежать требования связывания объектного кода.
Сообщение об ошибке компоновщика Linux правильно направило меня к строке dynamic_cast, но более ранние сообщения на этом форуме заставляли меня искать пропущенные реализации функций, а не реальную проблему: пропущенный объектный код. Мой обходной путь состоял в том, чтобы заменить функцию виртуального типа в базовом и производном классе, например virtual int isSpecialType (), вместо использования dynamic_cast. Этот метод позволяет избежать необходимости связывать код реализации объекта только для того, чтобы заставить dynamic_cast работать должным образом.
источник
В базовом классе (абстрактный базовый класс) вы объявляете виртуальный деструктор и, поскольку вы не можете объявить деструктор как чисто виртуальную функцию, вы также должны определить его прямо здесь, в абстрактном классе, просто фиктивное определение, подобное виртуальному ~ base ( ) {} или в любом производном классе.
Если вы не сделаете этого, вы получите «неопределенный символ» во время ссылки. Поскольку VMT имеет запись для всех чисто виртуальных функций с совпадающим NULL, он обновляет таблицу в зависимости от реализации в производном классе. Но для не чистых, но виртуальных функций, ему нужно определение во время соединения, чтобы можно было обновить таблицу VMT.
Используйте C ++ Filter, чтобы разобрать символ. Например, $ c ++ Filter _ZTIN10storageapi8BaseHostE выведет что-то вроде «typeinfo для storageapi :: BaseHost».
источник
Я получил много этих ошибок только сейчас. Случилось так, что я разделил класс только для заголовочного файла на заголовочный файл и файл cpp. Однако я не обновил свою систему сборки, поэтому файл cpp не был скомпилирован. Среди просто неопределенных ссылок на функции, объявленные в заголовке, но не реализованные, я получил много этих ошибок типа info.
Решением было перезапустить систему сборки, чтобы скомпилировать и связать новый файл cpp.
источник
в моем случае я использовал стороннюю библиотеку с заголовочными файлами и другими файлами. я подклассифицировал один класс, и ошибка ссылки, как это произошло, когда я пытаюсь создать экземпляр своего подкласса.
как упомянул @sergiy, зная, что это может быть проблемой 'rtti', мне удалось обойти ее, поместив реализацию конструктора в отдельный файл .cpp и применив к нему флаги компиляции '-fno-rtti' . это работает хорошо.
так как я все еще не совсем понимаю внутреннюю ошибку этой ссылки, я не уверен, является ли мое решение общим. Тем не менее, я думаю, что стоит попробовать, прежде чем попробовать адаптер, как упомянуто @francois. и, конечно, если все исходные коды доступны (не в моем случае), лучше перекомпилируйте с '-frtti', если это возможно.
Еще одна вещь, если вы решите попробовать мое решение, попробуйте сделать отдельный файл как можно более простым и не использовать некоторые изящные функции C ++. уделять особое внимание вещам, связанным с повышением, потому что многое зависит от ртти.
источник
У меня та же ошибка, когда моему интерфейсу (со всеми чисто виртуальными функциями) требовалась еще одна функция, и я забыл обнулить ее.
я имел
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 не является виртуальным, поэтому скомпилированный не знал, где взять реализацию для него, и поэтому запутался. мое сообщение было:
Простое изменение от
в
исправил проблему. Надеюсь, поможет
источник
Я сталкиваюсь с редкой ситуацией, но это может помочь другим друзьям в подобной ситуации. Я должен работать на более старой системе с gcc 4.4.7. Я должен скомпилировать код с поддержкой c ++ 11 или выше, поэтому я собираю последнюю версию gcc 5.3.0. При сборке моего кода и создании ссылок на зависимости, если зависимость создается с помощью более старого компилятора, я получил ошибку «неопределенная ссылка на», хотя я четко определил путь компоновки с помощью -L / path / to / lib -llibname. Некоторые пакеты, такие как boost и проекты, собранные с помощью cmake, обычно имеют тенденцию использовать более старый компилятор, и они обычно вызывают такие проблемы. Вы должны пройти долгий путь, чтобы убедиться, что они используют более новый компилятор.
источник
В моем случае это просто проблема зависимости от библиотеки, даже если у меня есть вызов dynamic_cast. После добавления достаточного количества зависимостей в make-файл эта проблема исчезла.
источник
Проверьте, что ваши зависимости были скомпилированы без
-f-nortti
.Для некоторых проектов вы должны установить это явно, как в RocksDB:
источник
В моем случае это была виртуальная функция в классе интерфейса, которая не была определена как чисто виртуальная.
Я забыл
= 0
немного.источник