未定义的引用vtable
所以,我得到了下面的代码(这个类是CGameModule)的臭名昭着的可怕的“未定义的参考”vtable …“的错误,我不能为了我的生活理解问题是什么。 起初,我以为是忘记把一个虚拟的功能当作一个机构,但据我所知,一切都在这里。 继承链有点长,但是这里是相关的源代码。 我不确定我应该提供哪些其他信息。
注意:构造函数就是这个错误发生的地方,看起来似乎。
我的代码:
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
GCC FAQ有一个条目:
解决方案是确保所有不纯的虚拟方法都被定义。 请注意,即使声明为纯虚拟的[class.dtor] / 7,也必须定义析构函数。
对于什么是值得的,在虚拟析构函数中遗忘一个主体会产生以下结果:
对'CYourClass'的vtable有未定义的引用。
我添加一个便条,因为错误信息是欺骗性的。 (这是与gcc版本4.6.3。)
所以,我已经找到了这个问题,这是一个坏逻辑的组合,不完全熟悉automake / autotools世界。 我将正确的文件添加到我的Makefile.am模板中,但是我不确定在构建过程中哪一步实际上创建了makefile文件本身。 所以,我正在编译一个旧的makefile,不知道我的新文件是什么。
感谢您的回复以及GCC FAQ的链接。 我一定会阅读,以避免这个问题发生的真正原因。
由于以下情况也可能导致对vtable的未定义的引用。 试试这个:
A类包含:
virtual void functionA(parameters)=0; virtual void functionB(parameters);
B类包含:
- 上述函数的定义
- 上面的函数B的定义。
C类包含:现在你正在写一个C类,你将从A类派生它。
现在,如果你尝试编译,你会得到对C类的vtable的Undefined引用,因为错误。
原因:
functionA
被定义为纯虚functionA
,其定义在类B中提供。 functionB
B被定义为虚functionB
(NOT PURE VIRTUAL),所以它试图在类A本身中找到它的定义,但是你在类B中提供了它的定义。
解:
- 使函数B为纯虚拟(如果您有这样的要求)
virtual void functionB(parameters) =0;
(这个工作是测试) - 为A类中的functionB提供定义,使其保持虚拟。 (希望它的作品,因为我没有尝试这个)
我只是得到这个错误,因为我的cpp文件不在makefile中。
如果您使用Qt,请尝试重新运行qmake。 如果这个错误出现在小部件的类中,qmake可能没有注意到应该重新生成ui类的vtable。 这为我解决了这个问题。
我只是遇到了这个错误,你可以检查的另一个原因。
基类定义了一个纯虚函数 :
virtual int foo(int x = 0);
而这个小类也有
int foo(int x) override;
问题是"=0"
被认为是在括号之外的错字:
virtual int foo(int x) = 0;
所以,如果你滚动这么远,你可能没有找到答案 – 这是别的东西来检查。
GNU C ++编译器必须决定放置vtable
位置,以便在多个编译单元中定义对象的虚拟函数(例如,某些对象的虚拟函数定义位于另一个.cpp文件的其他对象中.cpp文件等)。
编译器选择将vtable
放在第一个声明的虚函数被定义的地方。
现在,如果由于某种原因忘记提供对象中声明的第一个虚函数的定义(或者在连接阶段错误地忘记添加编译对象),您将会得到这个错误。
作为一个副作用,请注意,只有这个特殊的虚函数,你不会得到传统的链接器错误,就像你缺少函数foo 。
如果忘记链接到具有定义的对象文件,这可能会很容易发生。
不要过帐,但。 如果你正在处理继承 ,第二次谷歌命中是我曾经错过了,即。 所有的虚拟方法都应该被定义。
如:
virtual void fooBar() = 0;
有关详细信息,请参阅answare C ++ Undefined参考vtable和继承 。 刚刚意识到它已经在上面提到了,但是它可能会帮助别人。
- 你确定
CDasherComponent
有一个析构函数体吗? 这绝对不是在这里 – 问题是如果它在.cc文件。 - 从风格的角度来看,
CDasherModule
应该明确定义它的析构函数virtual
。 - 它看起来像
CGameModule
有一个额外的}
(在}; // for the class
之后)。 -
CGameModule
是否与定义CDasherModule
和CDasherComponent
的库链接?
这是一个可怕的错误,因为似乎人们似乎认为这个错误的原因是一些武断的东西。 例如,有人说你必须有基础的虚拟非纯粹析构函数,而其他人则推测这个析构函数必须在它自己的cpp文件中,而另一个说法是包含文件的顺序导致错误! 这些原因似乎并不正确。
如果一切都失败了,那么调试这个错误的一种方法是构建最小化的程序,它编译并保持更改,以便达到所需的状态。 在这之间,继续编译以查看何时开始失败。
所以,让我来介绍一个编译好的完整程序,使用C ++风格的接口,并有自己的cpp和头文件:
IBase.hpp
#pragma once class IBase { public: virtual void action() = 0; };
Derived.hpp
#pragma once #include "IBase.hpp" class Derived : public IBase { public: Derived(int a); void action() override; };
Derived.cpp
#include "Derived.hpp" Derived::Derived(int a) { } void Derived::action() {}
myclass.cpp
#include <memory> #include "Derived.hpp" class MyClass { public: MyClass(std::shared_ptr<Derived> newInstance) : instance(newInstance) { } void doSomething() { instance->action(); } private: std::shared_ptr<Derived> instance; }; int main(int argc, char** argv) { Derived myInstance(5); MyClass c(std::make_shared<Derived>(myInstance)); c.doSomething(); return 0; }
你可以像这样使用GCC编译这个:
g++ -std=c++11 -o a.out myclass.cpp Derived.cpp
请注意,这不需要任何虚拟的析构函数,构造函数或任何其他额外的文件来编译成功(尽管你应该拥有它们)。 您现在可以通过在IBase.hpp中删除= 0
来重现该错误。 我得到这个错误:
~/.../catkin_ws$ g++ -std=c++11 -o /tmp/m.out /tmp/myclass.cpp /tmp/Derived.cpp /tmp/cclLscB9.o: In function `IBase::IBase(IBase const&)': myclass.cpp:(.text._ZN5IBaseC2ERKS_[_ZN5IBaseC5ERKS_]+0x13): undefined reference to `vtable for IBase' /tmp/cc8Smvhm.o: In function `IBase::IBase()': Derived.cpp:(.text._ZN5IBaseC2Ev[_ZN5IBaseC5Ev]+0xf): undefined reference to `vtable for IBase' /tmp/cc8Smvhm.o:(.rodata._ZTI7Derived[_ZTI7Derived]+0x10): undefined reference to `typeinfo for IBase' collect2: error: ld returned 1 exit status
理解这个错误的方法如下:链接器正在寻找IBase的构造函数。 这将需要Derived的构造函数。 但是,由于派生覆盖方法从IBase,它有Vtable附加到它将引用IBase。 当链接器说“未定义引用VBase的vtable”它基本上意味着Derived有VBase的引用,但它找不到任何编译的IBase目标代码查找。 所以底线是IBase类没有实现的声明。 这意味着IBase中的方法被声明为虚拟的,但是我们忘记将其标记为纯虚拟的或提供其定义。
ROS和Catkin构建系统的注意事项
如果使用catkin构建系统在ROS中编译上面的一组类,那么在CMakeLists.txt中需要以下行:
add_executable(myclass src/myclass.cpp src/Derived.cpp) add_dependencies(myclass theseus_myclass_cpp) target_link_libraries(myclass ${catkin_LIBRARIES})
第一行基本上说,我们想要一个名为myclass的可执行文件,并且可以找到构建它的代码。 其中一个文件应该有main()。 请注意,您不必在CMakeLists.txt中的任何位置指定.hpp文件。 你也不需要指定Derived.cpp作为库。
也许缺少虚拟析构函数是促成因素?
virtual ~CDasherModule(){};
这是我的第一个搜索结果,所以我想我会添加另一件事来检查:确保虚拟函数的定义实际上在类。 就我而言,我有这样的:
头文件:
class A { public: virtual void foo() = 0; }; class B : public A { public: void foo() override; };
并在我的.cc文件中:
void foo() { ... }
这应该读
void B::foo() { }
好的,这个解决方案就是你可能错过了定义。 请参阅下面的示例以避免vtable编译器错误:
// In the CGameModule.h class CGameModule { public: CGameModule(); ~CGameModule(); virtual void init(); }; // In the CGameModule.cpp #include "CGameModule.h" CGameModule::CGameModule() { } CGameModule::~CGameModule() { } void CGameModule::init() // Add the definition { }
也许不是。 肯定~CDasherModule() {}
缺失。
这里有很多答案,但没有一个似乎涵盖了我的问题。 我有以下几点:
class I { virtual void Foo()=0; };
而在另一个文件(当然包括在汇编和链接中)
class C : public I{ void Foo() { //bar } };
那么这不起作用,我得到了每个人都在谈论的错误。 为了解决这个问题,我不得不将Foo的实际定义从类声明中移出来:
class C : public I{ void Foo(); }; C::Foo(){ //bar }
我不是C ++大师,所以我不能解释为什么这是更正确的,但它解决了我的问题。
我在下面的情况下得到这个错误
考虑一个你已经在头文件本身定义了一个类的成员函数的实现的情况。 这个头文件是一个导出的头文件(换句话说,它可能被复制到一些常见的/包含在你的代码库中)。 现在你已经决定把成员函数的实现分离成.cpp文件。 在将实现分离/移动到.cpp之后,头文件现在只有类内成员函数的原型。 上述变化后,如果你建立你的代码库,你可能会得到“未定义的参考”vtable …“错误。
为了解决这个问题,在构建之前,请确保删除common / include目录中的头文件(对其进行更改)。 还要确保你改变你的makefile来容纳/添加刚刚创建的新的.cpp文件构建的新的.o文件。 当你做这些步骤时,编译器/链接器将不再抱怨。
如果一切都失败,寻找重复。 直到我在另一篇文章中读到参考文献之前,我被明确的初始引用构造函数和析构函数误导了。 这是任何未解决的方法。 在我的情况下,我想我已经用char * xml作为参数替换了一个使用不必要的const char * xml的参数,但是我创建了一个新的,并将另一个留在原地。
有很多的可能性引起这个错误,我敢肯定,他们中的许多确实会导致错误。 就我而言,由于源文件的重复,还有另一个同一类的定义。 这个文件被编译,但没有链接,所以链接器抱怨无法找到它。
总结一下,我会说,如果你已经盯着这个类足够长的时间,看不到可能的语法问题可能导致它,寻找像缺少的文件或重复的文件的生成问题。
所以我在Windows XP和MinGW编译器上使用Qt,这让我疯狂。
基本上moc_xxx.cpp即使在我被添加时也是空的
Q_OBJECT
删除一切使虚拟,显式和任何你猜的功能不起作用。 最后我开始逐行删除,结果证明我已经删除了
#ifdef something
在文件周围。 即使#ifdef是真的moc文件没有生成。
所以删除所有#ifdefs修复了这个问题。
这个事情没有发生在Windows和VS 2013。
你也可能会收到类似的消息
SomeClassToTest.host.o: In function `class1::class1(std::string const&)': class1.hpp:114: undefined reference to `vtable for class1' SomeClassToTest.host.o: In function `class1::~class1()': class1.hpp:119: undefined reference to `vtable for class1' collect2: error: ld returned 1 exit status [link] FAILED: 'g++' '-o' 'stage/tests/SomeClassToTest' 'object/tests/SomeClassToTest.host.o' 'object/tests/FakeClass1.SomeClassToTest.host.o'
如果您在尝试链接另一个类SomeClass的单元测试时忘记定义类FakeClass1的虚函数。
//class declaration in class1.h class class1 { public: class1() { } virtual ~class1() { } virtual void ForgottenFunc(); };
和
//class definition in FakeClass1.h //... //void ForgottenFunc() {} is missing here
在这种情况下,我建议你再次检查你的假的class1。 你可能会发现你可能ForgottenFunc
在你的假类中定义一个虚函数ForgottenFunc
。
我遇到这种类型的错误,当我试图链接到一个对象,当我有一个错误,防止对象被添加到存档。
说我有libXYZ.a应该在int中有bioseq.o,但它没有。
我得到一个错误:
combineseq.cpp:(.text+0xabc): undefined reference to `vtable for bioseq'
这与以上所有不同。 我会在归档问题中调用这个缺失的对象。
在我的情况下,我正在使用Qt,并在foo.cpp
(而不是.h
)文件中定义了一个QObject
子类。 解决的办法是在#include "foo.moc"
的末尾添加#include "foo.moc"
。
当我将第二个类添加到现有的源/标头对时,出现此错误。 在同一个.h文件中有两个类头文件,在同一个.cpp文件中有两个类的函数定义。
之前我已经成功地完成了这个课程,而这些课程是密切合作的,但显然这次我不喜欢我。 仍然不知道是什么,但是每个编译单元把它们分成一个类就固定了。
失败的尝试:
_gui_icondata.h:
#ifndef ICONDATA_H #define ICONDATA_H class Data; class QPixmap; class IconData { public: explicit IconData(); virtual ~IconData(); virtual void setData(Data* newData); Data* getData() const; virtual const QPixmap* getPixmap() const = 0; void toggleSelected(); void toggleMirror(); virtual void updateSelection() = 0; virtual void updatePixmap(const QPixmap* pixmap) = 0; protected: Data* myData; }; //-------------------------------------------------------------------------------------------------- #include "_gui_icon.h" class IconWithData : public Icon, public IconData { Q_OBJECT public: explicit IconWithData(QWidget* parent); virtual ~IconWithData(); virtual const QPixmap* getPixmap() const; virtual void updateSelection(); virtual void updatePixmap(const QPixmap* pixmap); signals: public slots: }; #endif // ICONDATA_H
_gui_icondata.cpp:
#include "_gui_icondata.h" #include "data.h" IconData::IconData() { myData = 0; } IconData::~IconData() { if(myData) { myData->removeIcon(this); } //don't need to clean up any more; this entire object is going away anyway } void IconData::setData(Data* newData) { if(myData) { myData->removeIcon(this); } myData = newData; if(myData) { myData->addIcon(this, false); } updateSelection(); } Data* IconData::getData() const { return myData; } void IconData::toggleSelected() { if(!myData) { return; } myData->setSelected(!myData->getSelected()); updateSelection(); } void IconData::toggleMirror() { if(!myData) { return; } myData->setMirrored(!myData->getMirrored()); updateSelection(); } //-------------------------------------------------------------------------------------------------- IconWithData::IconWithData(QWidget* parent) : Icon(parent), IconData() { } IconWithData::~IconWithData() { } const QPixmap* IconWithData::getPixmap() const { return Icon::pixmap(); } void IconWithData::updateSelection() { } void IconWithData::updatePixmap(const QPixmap* pixmap) { Icon::setPixmap(pixmap, true, true); }
再一次,添加一个新的源/头对,并逐字地将IconWithData类剪切/粘贴到“刚刚工作”的位置。
我得到这个错误只是因为构造函数参数的名称在头文件和实现文件中不同。 构造函数签名是
PointSet (const PointSet & pset, Parent * parent = 0);
以及我在实施中所写的内容
PointSet (const PointSet & pest, Parent * parent)
因此我意外地把“pset”换成了“pest”。 编译器在抱怨这一个和另外两个没有错误的构造函数。 我在Ubuntu下使用g ++版本4.9.1。 而在这个派生类中定义一个虚拟析构函数没有什么区别(它是在基类中定义的)。 如果我没有在头文件中粘贴构造函数的主体,就不会发现这个错误,从而在类中定义它们。