При сборке моей программы на C ++ я получаю сообщение об ошибке
неопределенная ссылка на 'vtable ...
В чем причина этой проблемы? Как мне это исправить?
Так получилось, что я получаю сообщение об ошибке для следующего кода (рассматриваемый класс - CGameModule.), И я не могу на всю жизнь понять, в чем проблема. Сначала я думал, что это связано с забвением придания виртуальной функции тела, но, насколько я понимаю, все здесь. Цепочка наследования немного длинна, но вот соответствующий исходный код. Я не уверен, какую другую информацию я должен предоставить.
Примечание: конструктор, где эта ошибка происходит, казалось бы.
Мой код:
class CGameModule : public CDasherModule {
public:
CGameModule(Dasher::CEventHandler *pEventHandler, CSettingsStore *pSettingsStore, CDasherInterfaceBase *pInterface, ModuleID_t iID, const char *szName)
: CDasherModule(pEventHandler, pSettingsStore, iID, 0, szName)
{
g_pLogger->Log("Inside game module constructor");
m_pInterface = pInterface;
}
virtual ~CGameModule() {};
std::string GetTypedTarget();
std::string GetUntypedTarget();
bool DecorateView(CDasherView *pView) {
//g_pLogger->Log("Decorating the view");
return false;
}
void SetDasherModel(CDasherModel *pModel) { m_pModel = pModel; }
virtual void HandleEvent(Dasher::CEvent *pEvent);
private:
CDasherNode *pLastTypedNode;
CDasherNode *pNextTargetNode;
std::string m_sTargetString;
size_t m_stCurrentStringPos;
CDasherModel *m_pModel;
CDasherInterfaceBase *m_pInterface;
};
Наследует от ...
class CDasherModule;
typedef std::vector<CDasherModule*>::size_type ModuleID_t;
/// \ingroup Core
/// @{
class CDasherModule : public Dasher::CDasherComponent {
public:
CDasherModule(Dasher::CEventHandler * pEventHandler, CSettingsStore * pSettingsStore, ModuleID_t iID, int iType, const char *szName);
virtual ModuleID_t GetID();
virtual void SetID(ModuleID_t);
virtual int GetType();
virtual const char *GetName();
virtual bool GetSettings(SModuleSettings **pSettings, int *iCount) {
return false;
};
private:
ModuleID_t m_iID;
int m_iType;
const char *m_szName;
};
Который наследует от ....
namespace Dasher {
class CEvent;
class CEventHandler;
class CDasherComponent;
};
/// \ingroup Core
/// @{
class Dasher::CDasherComponent {
public:
CDasherComponent(Dasher::CEventHandler* pEventHandler, CSettingsStore* pSettingsStore);
virtual ~CDasherComponent();
void InsertEvent(Dasher::CEvent * pEvent);
virtual void HandleEvent(Dasher::CEvent * pEvent) {};
bool GetBoolParameter(int iParameter) const;
void SetBoolParameter(int iParameter, bool bValue) const;
long GetLongParameter(int iParameter) const;
void SetLongParameter(int iParameter, long lValue) const;
std::string GetStringParameter(int iParameter) const;
void SetStringParameter(int iParameter, const std::string & sValue) const;
ParameterType GetParameterType(int iParameter) const;
std::string GetParameterName(int iParameter) const;
protected:
Dasher::CEventHandler *m_pEventHandler;
CSettingsStore *m_pSettingsStore;
};
/// @}
#endif
qmake -project
и затемqmake
) для генерации новогоMakefile
, это вероятный источник ошибки при использовании Qt.Q_OBJECT
копируется извне, но еще не является частью файла .pro, то, хотя он компилируется нормально, он не связывается. Мы должны добавить этот.h/.cpp
файл в .pro файл, чтобы иметь возможностьqmake
.Ответы:
В FAQ GCC есть запись:
источник
nm -C CGameModule.o | grep CGameModule::
перечислит методы, которые определены, при условии, что вся ваша реализация класса идет в файл логического объекта. Вы можете сравнить это с тем, что определено как виртуальное, чтобы выяснить, что вы пропустили.Для чего стоит забыть о теле виртуального деструктора, получится следующее:
Я добавляю заметку, потому что сообщение об ошибке обманчиво. (Это было с версией gcc 4.6.3.)
источник
~Destructor = default;
в заголовочном файле не помогло. Есть ли задокументированная ошибка, поданная против gcc?Итак, я разобрался с проблемой, и это была комбинация плохой логики и того, что я не был полностью знаком с миром automake / autotools. Я добавлял правильные файлы в свой шаблон Makefile.am, но я не был уверен, какой шаг в нашем процессе сборки фактически создал сам make-файл. Итак, я компилировал старый make-файл, который не имел никакого представления о моих новых файлах.
Спасибо за ответы и ссылку на GCC FAQ. Я обязательно прочту это, чтобы избежать этой проблемы по реальной причине.
источник
Если вы используете Qt, попробуйте перезапустить qmake. Если эта ошибка относится к классу виджета, qmake, возможно, не заметил, что vtable класса ui должен быть восстановлен. Это исправило проблему для меня.
источник
qmake
, у меня было то же самоеcmake
. Частично проблема может заключаться в том, что оба инструмента имеют небольшую проблему с заголовочными файлами, которые не всегда могут вызвать перестроение при необходимости.Неопределенная ссылка на vtable может также возникнуть из-за следующей ситуации. Просто попробуйте это:
Класс А содержит:
Класс B содержит:
Класс C содержит: теперь вы пишете класс C, в котором вы собираетесь извлечь его из класса A.
Теперь, если вы попытаетесь скомпилировать, вы получите неопределенную ссылку на vtable для класса C как ошибку.
Причина:
functionA
определяется как чисто виртуальный, и его определение предоставляется в классе B.functionB
определяется как виртуальное (NOT PURE VIRTUAL), поэтому он пытается найти свое определение в самом классе A, но вы предоставили его определение в классе B.Решение:
virtual void functionB(parameters) =0;
(это работает, проверено)источник
Я просто получил эту ошибку, потому что мой файл cpp не был в make-файле.
источник
undefined reference to {function/class/struct}
когда естьvirtual
вещи, связанные с этим. Выкинул меня.Что такое
vtable
?Может быть полезно узнать, о чем идет речь в сообщении об ошибке, прежде чем пытаться это исправить. Я начну с высокого уровня, а затем рассмотрю некоторые детали. Таким образом, люди могут пропустить вперед, как только они освоятся с пониманием vtables. ... и там идет группа людей, пропускающих прямо сейчас. :) Для тех, кто торчит:
Vtable в основном является наиболее распространенной реализацией полиморфизма в C ++ . Когда используются vtables, у каждого полиморфного класса есть vtable где-нибудь в программе; Вы можете думать об этом как о (скрытом)
static
члене данных класса. Каждый объект полиморфного класса связан с vtable для его самого производного класса. Проверяя эту связь, программа может проявить свою полиморфную магию. Важное предупреждение: vtable - это деталь реализации. Это не предусмотрено стандартом C ++, хотя большинство (все?) Компиляторов C ++ используют vtables для реализации полиморфного поведения. Детали, которые я представляю, являются либо типичными, либо разумными подходами. Компиляторы могут отклоняться от этого!Каждый полиморфный объект имеет (скрытый) указатель на vtable для самого производного класса объекта (возможно, несколько указателей, в более сложных случаях). Посмотрев на указатель, программа может сказать, что такое «настоящий» тип объекта (кроме как во время конструирования, но давайте пропустим этот особый случай). Например, если объект типа
A
не указывает на vtable ofA
, то этот объект на самом деле является подобъектом чего-то, полученного изA
.Название «виртуальные таблицы» происходит от « об irtual функции таблицы ». Это таблица, в которой хранятся указатели на (виртуальные) функции. Компилятор выбирает свое соглашение о том, как таблица составлена; простой подход состоит в том, чтобы пройти через виртуальные функции в порядке, в котором они объявлены в определениях классов. Когда вызывается виртуальная функция, программа следует за указателем объекта на vtable, переходит к записи, связанной с требуемой функцией, затем использует указатель сохраненной функции для вызова правильной функции. Существуют различные приемы для создания этой работы, но я не буду вдаваться в подробности.
Где / когда
vtable
генерируется?Vtable автоматически генерируется (иногда называемый "испускаемым") компилятором. Компилятор может генерировать виртуальную таблицу в каждом модуле перевода, который видит определение полиморфного класса, но это, как правило, будет излишним излишним. Альтернатива ( используемая gcc и, возможно, другими) заключается в том, чтобы выбрать одну единицу трансляции, в которую нужно поместить виртуальную таблицу, подобно тому, как вы выбираете один исходный файл, в который помещаются статические члены данных класса. Если этот процесс выбора не может выбрать какие-либо единицы перевода, тогда vtable становится неопределенной ссылкой. Отсюда и ошибка, сообщение которой по общему признанию не особо ясно.
Точно так же, если процесс выбора выбирает единицу перевода, но этот объектный файл не предоставляется компоновщику, тогда vtable становится неопределенной ссылкой. К сожалению, сообщение об ошибке может быть даже менее четким в этом случае, чем в случае неудачного процесса выбора. (Спасибо ответчикам, которые упомянули эту возможность. Я, вероятно, забыл бы это иначе.)
Процесс выбора, используемый gcc, имеет смысл, если мы начнем с традиции выделять (один) исходный файл каждому классу, который нужен для его реализации. Было бы неплохо создать vtable при компиляции этого исходного файла. Давайте назовем это нашей целью. Однако процесс отбора должен работать, даже если эта традиция не будет соблюдена. Поэтому вместо того, чтобы искать реализацию всего класса, давайте посмотрим на реализацию конкретного члена класса. Если следовать традициям - и если этот член действительно реализован - тогда это достигает цели.
Элемент, выбранный gcc (и, возможно, другими компиляторами), является первой не встроенной виртуальной функцией, которая не является чисто виртуальной. Если вы являетесь частью толпы, которая объявляет конструкторы и деструкторы перед другими функциями-членами, то этот деструктор имеет хорошие шансы быть выбранным. (Вы не забыли сделать виртуальный деструктор, верно?) Есть исключения; Я ожидаю, что наиболее распространенными исключениями являются случаи, когда для деструктора предоставляется встроенное определение и когда запрашивается деструктор по умолчанию (с использованием «
= default
»).Проницательный может заметить, что полиморфному классу разрешено предоставлять встроенные определения для всех его виртуальных функций. Разве это не вызывает сбой процесса выбора? Это происходит в старых компиляторах. Я читал, что последние компиляторы справились с этой ситуацией, но я не знаю соответствующих номеров версий. Я мог бы попытаться найти это, но легче либо кодировать его, либо ждать, пока компилятор пожалуется.
Таким образом, существует три основных причины ошибки «неопределенная ссылка на vtable»:
Эти причины сами по себе недостаточны, чтобы вызвать ошибку самостоятельно. Скорее, это то, к чему вы обратились бы, чтобы устранить ошибку. Не ожидайте, что намеренное создание одной из этих ситуаций определенно приведет к этой ошибке; Есть и другие требования. Ожидайте, что разрешение этих ситуаций разрешит эту ошибку.
(ОК, номер 3, возможно, было достаточно, когда был задан этот вопрос.)
Как исправить ошибку?
Добро пожаловать обратно, люди пропускают вперед! :)
= 0
") и определение которой вы предоставляете (не "= default
").Пример
Детали того, что нужно сделать, могут различаться и иногда разбиваться на отдельные вопросы (например, Что такое неопределенная ссылка / нерешенная внешняя ошибка символа и как ее исправить? ). Тем не менее, я приведу пример того, что делать в конкретном случае, который может сбить с толку новых программистов.
Шаг 1 упоминает изменение вашего класса, чтобы он имел функцию определенного типа. Если описание этой функции перешло вам голову, вы можете оказаться в ситуации, которую я собираюсь рассмотреть. Имейте в виду, что это способ достижения цели; это не единственный способ, и в вашей конкретной ситуации легко могут быть лучшие способы. Давайте назовем ваш класс
A
. Ваш деструктор объявлен (в вашем определении класса) какили
? Если это так, два шага изменит ваш деструктор на тот тип функции, который мы хотим. Во-первых, измените эту строку на
Во-вторых, поместите следующую строку в исходный файл, который является частью вашего проекта (предпочтительно файл с реализацией класса, если он у вас есть):
Это делает ваш (виртуальный) деструктор не встроенным и не генерируемым компилятором. (Не стесняйтесь изменять вещи, чтобы они лучше соответствовали вашему стилю форматирования кода, например, добавив комментарий заголовка к определению функции.)
источник
Здесь много спекуляций в разных ответах. Ниже я приведу довольно минимальный код, который воспроизводит эту ошибку, и объясню, почему она возникает.
Довольно минимальный код для воспроизведения этой ошибки
IBase.hpp
Derived.hpp
Derived.cpp
myclass.cpp
Вы можете скомпилировать это с помощью GCC следующим образом:
Теперь вы можете воспроизвести ошибку, удалив ее
= 0
в IBase.hpp. Я получаю эту ошибку:объяснение
Обратите внимание, что приведенный выше код не требует никаких виртуальных деструкторов, конструкторов или любых других дополнительных файлов для успешной компиляции (хотя они должны быть у вас).
Способ понять эту ошибку следующим образом: Линкер ищет конструктор IBase. Это понадобится для конструктора Derived. Однако поскольку Derived переопределяет методы из IBase, к нему прикреплен vtable, который будет ссылаться на IBase. Когда компоновщик говорит «неопределенная ссылка на vtable для IBase», это в основном означает, что Derived имеет vtable ссылку на IBase, но он не может найти какой-либо скомпилированный объектный код IBase для поиска. Итак, суть в том, что у класса IBase есть объявления без реализаций. Это означает, что метод в IBase объявлен как виртуальный, но мы забыли пометить его как чисто виртуальный ИЛИ предоставить его определение.
Прощание Совет
Если ничего не помогает, то одним из способов отладки этой ошибки является создание минимальной программы, которая выполняет компиляцию, а затем продолжает изменять ее, чтобы она достигла желаемого состояния. В промежутках, продолжайте компиляцию, чтобы увидеть, когда он начинает терпеть неудачу.
Обратите внимание на систему сборки ROS и Catkin
Если вы компилировали вышеуказанный набор классов в ROS с использованием системы сборки catkin, вам понадобятся следующие строки в CMakeLists.txt:
Первая строка в основном говорит, что мы хотим создать исполняемый файл с именем myclass, а код для его сборки можно найти в следующих файлах. Один из этих файлов должен иметь main (). Обратите внимание, что вам не нужно указывать файлы .hpp в любом месте CMakeLists.txt. Также вам не нужно указывать Derived.cpp в качестве библиотеки.
источник
Я просто столкнулся с другой причиной этой ошибки, которую вы можете проверить.
Базовый класс определил чисто виртуальную функцию как:
И подкласс
Проблема заключалась в опечатке, которая
"=0"
должна была быть за скобками:Так что, если вы прокручиваете это так далеко внизу, вы, вероятно, не нашли ответ - это что-то еще, чтобы проверить.
источник
Это может произойти довольно легко, если вы забудете связать объектный файл с определением.
источник
Компилятор GNU C ++ должен принять решение, куда его поместить,
vtable
если у вас есть определение виртуальных функций объекта, распределенного по нескольким блокам компиляции (например, некоторые определения виртуальных функций объектов находятся в файле .cpp, а другие - в другом. файл cpp и т. д.).Компилятор решает поместить его
vtable
в то же место, где определена первая объявленная виртуальная функция.Теперь, если вы по какой-то причине забыли предоставить определение для этой первой виртуальной функции, объявленной в объекте (или ошибочно забыли добавить скомпилированный объект на этапе компоновки), вы получите эту ошибку.
Как побочный эффект, обратите внимание, что только для этой конкретной виртуальной функции вы не получите традиционную ошибку компоновщика, как если бы вы пропустили функцию foo .
источник
Не пересекать пост, но. Если вы имеете дело с наследованием, то второе попадание в Google было тем, что я пропустил, т.е. все виртуальные методы должны быть определены.
Такие как:
Посмотрите Anware C ++ Undefined Ссылка на vtable и наследование для деталей. Просто понял, что это уже упоминалось выше, но, черт возьми, это может кому-то помочь.
источник
Хорошо, решение этого в том, что вы, возможно, пропустили определение. Смотрите пример ниже, чтобы избежать ошибки компилятора vtable:
источник
CDasherComponent
есть тело для деструктора? Это определенно не здесь - вопрос в том, находится ли он в файле .cc.CDasherModule
следует явно определить его деструкторvirtual
.CGameModule
имеет дополнительный}
в конце (после}; // for the class
).CGameModule
он с библиотеками, которые определяютCDasherModule
иCDasherComponent
?источник
Возможно, отсутствующий виртуальный деструктор является фактором?
источник
Это был первый результат поиска для меня, поэтому я решил добавить еще одну вещь для проверки: убедиться, что определение виртуальных функций действительно в классе. В моем случае у меня было это:
Заголовочный файл:
и в моем .cc файле:
Это следует прочитать
источник
Не возможно. Определенно
~CDasherModule() {}
отсутствует.источник
Здесь так много ответов, но, похоже, ни один из них не охватил мою проблему. У меня было следующее:
И в другом файле (включенном в компиляцию и компоновку, конечно)
Ну, это не сработало, и я получил ошибку, о которой все говорят. Чтобы решить эту проблему, мне пришлось убрать фактическое определение Foo из объявления класса как таковое:
Я не гуру C ++, поэтому я не могу объяснить, почему это более правильно, но это решило проблему для меня.
источник
Поэтому я использовал Qt с Windows XP и компилятором MinGW, и это сводило меня с ума.
В основном moc_xxx.cpp был сгенерирован пустым, даже когда я был добавлен
Q_OBJECT
Удаление всего, что делает функции виртуальными, явными и все, что вы догадались, не сработало. Наконец я начал удалять построчно, и оказалось, что у меня
Вокруг файла. Даже когда #ifdef был истинным, файл moc не генерировался.
Так что удаление всех #ifdefs решило проблему.
Такого не было с Windows и VS 2013.
источник
g++ *.cpp ...
. (Нужно было что-то быстрое и грязное, но qmake был полон горя.)Если ничего не помогает, поищите дубликаты. Я был сбит с толку явной исходной ссылкой на конструкторы и деструкторы, пока не прочитал ссылку в другом посте. Это любой нерешенный метод. В моем случае я подумал, что заменил объявление, в котором в качестве параметра использовался char * xml, на использование ненужного хлопотного const char * xml, но вместо этого я создал новое и оставил другое на месте.
источник
Есть много возможностей, упомянутых для возникновения этой ошибки, и я уверен, что многие из них вызывают ошибку. В моем случае было другое определение того же класса из-за дублирования исходного файла. Этот файл был скомпилирован, но не связан, поэтому компоновщик жаловался на невозможность его найти.
Подводя итог, я бы сказал, что если вы достаточно долго смотрели на класс и не видите, какая возможная синтаксическая проблема может его вызывать, ищите проблемы сборки, такие как отсутствующий файл или дублированный файл.
источник
В моем случае я использую Qt и определил
QObject
подкласс вfoo.cpp
(не.h
) файле. Исправление было добавить#include "foo.moc"
в концеfoo.cpp
.источник
Я думаю, что также стоит упомянуть, что вы также получите сообщение, когда попытаетесь связать объект любого класса, который имеет хотя бы один виртуальный метод, и компоновщик не может найти файл. Например:
Foo.hpp:
foo.cpp:
Составлено с:
И main.cpp:
Составлено и связано с:
Дает нашу любимую ошибку:
Это произошло из-за моего непонимания:
Vtable создается для каждого класса во время компиляции
Линкер не имеет доступа к vtable, который есть в Foo.o
источник
Я получил эту ошибку в следующем сценарии
Рассмотрим случай, когда вы определили реализацию функций-членов класса в самом заголовочном файле. Этот заголовочный файл является экспортированным заголовком (другими словами, он может быть скопирован в какой-то общий файл / включен непосредственно в вашу кодовую базу). Теперь вы решили отделить реализацию функций-членов от файла .cpp. После того, как вы отделили / переместили реализацию в .cpp, в файле заголовка теперь есть только прототипы функций-членов внутри класса. После вышеупомянутых изменений, если вы создаете свою кодовую базу, вы можете получить ошибку «неопределенная ссылка на« vtable ... ».
Чтобы это исправить, перед сборкой убедитесь, что вы удалили заголовочный файл (в который вы внесли изменения) в общую / включаемую директорию. Также убедитесь, что вы изменили свой make-файл для размещения / добавления нового .o-файла, созданного из нового .cpp-файла, который вы только что создали. Когда вы выполните эти шаги, компилятор / компоновщик больше не будет жаловаться.
источник
Я получил этот тип ошибки в ситуациях, когда я пытался связать объект, когда получал ошибку make, которая препятствовала добавлению объекта в архив.
Скажем, у меня есть libXYZ.a, который должен иметь bioseq.o в int, но это не так.
Я получил ошибку:
Это отличается от всего вышеперечисленного. Я бы назвал этот отсутствующий объект в задаче архива.
источник
Также возможно, что вы получите сообщение как
если вы забыли определить виртуальную функцию класса FakeClass1 при попытке связать модульный тест для другого класса SomeClass.
А также
В этом случае я предлагаю вам еще раз проверить вашу подделку для class1. Вы, вероятно, обнаружите, что вы, возможно, забыли определить виртуальную функцию
ForgottenFunc
в вашем поддельном классе.источник
Я получил эту ошибку, когда я добавил второй класс в существующую пару источник / заголовок. Два заголовка класса в одном файле .h и определения функций для двух классов в одном файле .cpp.
Я делал это раньше успешно, с классами, которые должны работать в тесном контакте, но, похоже, мне что-то не понравилось в этот раз. Все еще не знаю что, но разделение их на один класс на единицу компиляции исправило это.
Неудачная попытка:
_gui_icondata.h:
_gui_icondata.cpp:
Снова, добавление новой пары источник / заголовок и сокращение / вставка дословно класса IconWithData туда "просто сработало".
источник
Мой случай был глупым, у меня был дополнительный
"
после#include
по ошибке, и угадайте, что?Я часами чесал голову и лицо, комментируя виртуальные функции, чтобы увидеть, изменилось ли что-нибудь, и, наконец, удалив лишнее
"
, все было исправлено! Такие вещи действительно должны приводить к ошибке компиляции, а не к ошибке ссылки.Под дополнительным
"
я имею в виду:источник
В моем случае у меня был базовый класс с именем Person и два производных класса с именами Student и Professor.
Как была исправлена моя программа: 1. Я сделал все функции в базовом классе
Pure Virtual.
2. Я использовал все виртуальные деструкторы какdefault ones.
источник
Я получил эту ошибку только потому, что имя аргумента конструктора различалось в заголовочном файле и в файле реализации. Подпись конструктора
и то, что я написал в реализации, началось с
таким образом я случайно заменил «pset» на «pest». Компилятор жаловался на этот и два других конструктора, в которых вообще не было ошибок. Я использую g ++ версии 4.9.1 под Ubuntu. И определение виртуального деструктора в этом производном классе не имеет значения (он определен в базовом классе). Я бы никогда не нашел эту ошибку, если бы не вставлял тела конструкторов в заголовочный файл, таким образом определяя их в классе.
источник