2008-11-21 3 views
154

Я просто наткнулся на следующую ошибку (и нашел решение в Интернете, но это не присутствует в Stack Overflow):г ++ неопределенная ссылка на TypeInfo

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

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

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

+15

Я знаю, что это старое сообщение, но сегодня у меня была такая же проблема, и решение было просто определить мою виртуальную функцию как виртуальный abc() {} в базовом классе, а не виртуальный abc(); который дал ошибку. – Nav 2010-11-30 09:04:41

+7

еще лучше как `virtual void abc() = 0;` (если базовая версия никогда не вызывается) – dhardy 2012-07-09 08:27:37

+3

@Nav: Если вы определяете `abc()` как это, вы можете легко забыть переопределить `abc()` в и думаю, что все в порядке, так как вы все равно можете вызвать функцию без каких-либо проблем. Хорошая практика для реализации чистых виртуальных функций содержится в [этой статье] (http://www.artima.com/cppsource/pure_virtual.html), и это должно сделать функцию print «Чистая виртуальная функция», а затем сбой программа. – HelloGoodbye 2012-09-27 11:44:38

ответ

163

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

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

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

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

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

Линия

virtual void fn(); 

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

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

extern int i; 
int *pi = &i; 

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

+23

Неверно утверждать, что `virtual void fn() = 0` является определением. Это не определение, а просто * объявление *. Единственная причина, по которой линкер не пытается его решить, заключается в том, что соответствующая запись VMT не будет ссылаться на тело функции (скорее всего, будет содержать указатель на нуль). Однако никто не запрещает вам вызывать эту чистую виртуальную функцию не виртуальным способом, т. Е. С использованием полностью квалифицированного имени. В этом случае линкер * будет * искать тело, и вам нужно будет определить функцию. И да, вы * можете * определить тело для чистой виртуальной функции. – AnT 2010-06-25 00:41:48

+1

И иногда даже один должен объявить тело чистой виртуальной функцией. – mark 2013-03-25 01:31:29

35

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

virtual void foo(); 

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

virtual void foo() {} 

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

virtual void foo() = 0; 
+5

Ты спас свой день. Легко забыть an = 0 ... – Lake 2015-04-23 14:56:19

11

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

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

22

Цитируя the gcc manual:

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

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

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

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

16

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

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

__attribute__ ((visibility("default"))) 

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

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

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

111

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

[source]

3

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

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

Используйте символ C++ для демонстрации символа. Подобно $ C++ filt_ZTIN10storageapi8BaseHostE выведет что-то вроде «typeinfo для storageapi :: BaseHost».

8

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

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

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

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

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

3

Как и в предыдущем обсуждении 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.

2

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

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

6

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

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

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

Для получения более подробной информации о порядке связи с gcc/g++ см. this SO question.

0

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

1

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

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

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

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

0

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

Я имел

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; 

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

Смежные вопросы