Undefined reference to vtable
所以,我变得非常可怕
undefined reference to 'vtable...
以下代码出错(相关类是cgamemodule),我一辈子都无法理解问题是什么。起初,我以为这和忘记给一个虚拟的身体功能有关,但据我所知,一切都在这里。继承链有点长,但这里是相关的源代码。我不知道该提供什么其他信息。
注意:看起来,构造函数就是这个错误发生的地方。
我的代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | 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; }; |
继承自…
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | 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; }; |
继承自……
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 | 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常见问题有一个条目:
The solution is to ensure that all virtual methods that are not pure are defined. Note that a destructor must be defined even if it is declared pure-virtual [class.dtor]/7.
对于它的价值,忘记虚拟析构函数上的实体会生成以下内容:
undefined reference to `vtable for CYourClass'.
我添加了一个注释,因为错误消息具有欺骗性。(这是GCC 4.6.3版。)
所以,我已经解决了这个问题,它结合了糟糕的逻辑,不完全熟悉汽车制造/自动工具的世界。我正在将正确的文件添加到makefile.am模板中,但我不确定构建过程中的哪个步骤实际创建了makefile本身。所以,我正在编译一个旧的makefile,它对我的新文件一无所知。
感谢您的回复和GCC常见问题的链接。我一定会读到这篇文章,以避免这个问题的发生是有真正原因的。
(P)如果你使用QT,try rerunning qmake。如果这一错误是在Widget's class中,QMake might have failed to notice that the UI class vtable should be registed.这是我的问题。(p)
由于以下情况,可能会出现对vtable的未定义引用。试一试:
A级包含:
1 2 | virtual void functionA(parameters)=0; virtual void functionB(parameters); |
B类包含:
类C包含:现在您正在编写一个类C,在其中您将从类A派生它。
现在,如果您试图编译,您将得到未定义的对类C的vtable的引用作为错误。
原因:
解决方案:
我只是得到了这个错误,因为我的cpp文件不在makefile中。
(P)我只是因为另一个原因这一错误你可以检查。(p)(P)基本分类将虚拟函数定义为:(p)字母名称(P)And the subclass had(p)字母名称(P)The problem was the typo that the EDOCX1 genital 3 sil was supposed to be outside of the parents:(p)字母名称(P)So,in case you're scrolling this far down,you probably did't find the answer-this is some else to check for.(p)
各种各样的答案中都有很多猜测。下面我将给出一个复制此错误的非常小的代码,并解释为什么会发生此错误。
复制此错误的代码非常少
Ibas.HPP
1 2 3 4 5 6 | #pragma once class IBase { public: virtual void action() = 0; }; |
推导出
1 2 3 4 5 6 7 8 9 | #pragma once #include"IBase.hpp" class Derived : public IBase { public: Derived(int a); void action() override; }; |
派生CPP
1 2 3 | #include"Derived.hpp" Derived::Derived(int a) { } void Derived::action() {} |
MyCase.CPP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | #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如下编译:
1 | g++ -std=c++11 -o a.out myclass.cpp Derived.cpp |
现在您可以通过删除ibase.hpp中的
1 2 3 4 5 6 7 | ~/.../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的构造函数。这将需要它作为派生的构造函数。但是,由于从IBase派生的重写方法,它附加了将引用IBase的vtable。当链接器说"未定义IBase对vtable的引用"时,它基本上意味着派生的具有对ibase的vtable引用,但找不到任何要查找的ibase编译对象代码。因此,底线是类IBase有没有实现的声明。这意味着IBase中的一个方法被声明为虚方法,但我们忘记了将其标记为纯虚方法或提供其定义。
分型头
如果所有其他方法都失败了,那么调试此错误的一种方法是生成可编译的最小程序,然后不断更改它,使其达到您想要的状态。在此期间,继续编译以查看它何时开始失败。
关于ROS和Catkin构建系统的说明
如果您使用catkin构建系统在ROS中编译上述一组类,那么您将需要在cmakelists.txt中使用以下行:
1 2 3 | 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指定为库。
如果忘记链接到具有定义的对象文件,则很容易发生这种情况。
(P)The GNU C++compiler has to make a decision where to put the EDOCX1 original 1 in case you have the definition of the virtual functions of an object spread across multiple compilations units(e.g.some of the objects vital functions definitions are in a CPP file,and so on).(p)(P)The compiler chooses to put the EDOCX1 universal 1 in the same place as the first declared virtual function is defined.(p)(P)Now if you for some reason forgot to provide a definition for that first vital function declared in the object(or mistakenly forgot to add the compiled object at linking phase),you will get this mistake.(p)(P)作为一个侧面效应,请注意,只有为这一特定的虚拟功能,你才不会得到传统的链接错误,就像你失去了功能。(p)
不是为了越界,而是。如果您要处理继承问题,那么第二次谷歌命中是我错过的,也就是说,应该定义所有虚拟方法。
例如:
1 | virtual void fooBar() = 0; |
有关详细信息,请参见CaseSc++对VTABLE和继承的未定义引用。刚刚意识到上面已经提到了,但见鬼,它可能会帮助某人。
好的,解决这个问题的方法是您可能遗漏了定义。请参阅下面的示例以避免vtable编译器错误:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | // 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 { } |
- 你确定
CDasherComponent 有一个毁灭者的尸体吗?它肯定不在这里-问题是它是否在.cc文件中。 - 从风格的角度来看,
CDasherModule 应该明确定义其析构函数virtual 。 - 看起来
CGameModule 的末尾(在}; // for the class 之后)有一个额外的} 。 CGameModule 是否与定义CDasherModule 和CDasherComponent 的库相关联?
这是我的第一个搜索结果,所以我想我要添加另一个检查:确保虚拟函数的定义实际上在类中。就我而言,我有:
头文件:
1 2 3 4 5 6 7 8 9 | class A { public: virtual void foo() = 0; }; class B : public A { public: void foo() override; }; |
在我的.cc文件中:
1 2 3 | void foo() { ... } |
这应该是
1 2 | void B::foo() { } |
(P)Perhaps missing the virtual destructor is contributing factor?(p)字母名称
所以我在WindowsXP和Mingw编译器上使用qt,这让我抓狂。
基本上,moc_xxx.cpp是空的,即使添加了我。
Q-对象
删除所有使函数虚拟化、显式和任何你认为不起作用的东西。最后我开始一行一行地删除,结果发现我
1 | #ifdef something |
在文件周围。即使ifdef是真的,也不会生成moc文件。
所以移除所有的ifdef就解决了这个问题。
这件事在Windows和VS 2013中没有发生。
这里有这么多答案,但似乎没有一个涵盖我的问题是什么。我有以下几点:
1 2 3 | class I { virtual void Foo()=0; }; |
在另一个文件中(当然包括在编译和链接中)
1 2 3 4 5 | class C : public I{ void Foo() { //bar } }; |
好吧,这不起作用,我得到了每个人都在说的错误。为了解决这个问题,我必须将foo的实际定义从类声明中移出,如下所示:
1 2 3 4 5 6 7 | class C : public I{ void Foo(); }; C::Foo(){ //bar } |
我不是C++大师,所以我不能解释为什么这是更正确的,但它解决了我的问题。
(P)不是Perhaps.你说的是什么?(p)
(P)There are a lot of possibilities mentioned for causing this mistake,and I'm sure many of the m do cause the mistake.In my case,there was another definition of the same class,due to a duplication of the source file.This file was compiled,but not linked,so the linker was complaining about being unable to find it.(p)(P)To summarize,I would say that if you've started a t the class long enough and can't see what possible syntax problem could be causing i t,look for build issues like a missing file or a duplicated file.(p)
在我的例子中,我使用qt并在
如果其他都失败了,寻找重复。我被对构造函数和析构函数的明确初始引用误导了,直到我在另一篇文章中读到了一个引用。这是任何未解决的方法。在我的例子中,我认为我已经用一个不必要的麻烦的constchar*xml替换了使用char*xml作为参数的声明,但是我创建了一个新的声明,并将另一个声明留在原处。
在我试图链接到一个对象的情况下,我得到了这种类型的错误,当我得到一个make bug时,它阻止了对象被添加到存档中。
假设我有libxyz.a,它应该在int中有bioseq.o,但它没有。
我有一个错误:
1 | combineseq.cpp:(.text+0xabc): undefined reference to `vtable for bioseq' |
这与上述所有情况不同。我会把这个丢失的对象称为归档问题。
我在下面的场景中得到了这个错误
考虑这样一种情况,您已经在头文件本身中定义了类的成员函数的实现。这个头文件是一个导出的头文件(换句话说,它可能被复制到代码库中的一些公共/包含中)。现在您决定将成员函数的实现分离到.cpp文件。将实现分离/移动到.cpp后,头文件现在只具有类内成员函数的原型。在上面的更改之后,如果您构建了代码库,您可能会得到"未定义的对‘vtable…’的引用"错误。
要解决这个问题,在构建之前,请确保删除common/include目录中的头文件(对其进行了更改)。还要确保将make file更改为适应/添加从刚创建的新.cpp文件生成的新.o文件。当您执行这些步骤时,编译器/链接器将不再抱怨。
我认为还值得一提的是,当您试图链接到任何类的对象时,如果该类至少有一个虚拟方法,并且链接器找不到该文件,那么您也会收到消息。例如:
Foo.hpp:
1 2 3 4 5 | class Foo { public: virtual void StartFooing(); }; |
Foo.cpp:
1 2 3 | #include"Foo.hpp" void Foo::StartFooing(){ //fooing } |
编译:
1 | g++ Foo.cpp -c |
CPP:
1 2 3 4 5 6 | #include"Foo.hpp" int main() { Foo foo; } |
编译并链接到:
1 | g++ main.cpp -o main |
给出了我们最喜欢的错误:
/tmp/cclKnW0g.o: In function
main': main.cpp:(.text+0x1a): undefined vtable for Foo' collect2: error: ld returned 1 exit
reference to
status
这是我不为人知的秘密,因为:
vtable在编译时按类创建
链接器无法访问foo.o中的vtable。
你也有可能收到这样的信息
1 2 3 4 5 6 | 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的虚拟函数。
1 2 3 4 5 6 7 8 9 10 11 12 | //class declaration in class1.h class class1 { public: class1() { } virtual ~class1() { } virtual void ForgottenFunc(); }; |
和
1 2 3 | //class definition in FakeClass1.h //... //void ForgottenFunc() {} is missing here |
在这种情况下,我建议你再检查一下你的1级假货。您可能会发现您可能忘记了在您的伪类中定义一个虚拟函数
当我向现有的源/头对添加第二个类时,出现了这个错误。同一.h文件中的两个类头,以及同一.cpp文件中两个类的函数定义。
我以前已经成功地完成了这项工作,课程本来是要紧密合作的,但显然这次有些事情不喜欢我。仍然不知道是什么,但是把它们分成每个编译单元一个类就可以解决这个问题。
失败的尝试:
_ gui ou iconda.h.图:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | #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_iconda.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | #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类逐字剪切/粘贴到其中"刚刚工作"。
(P)I got this mistake just because the name of a constructor argument differed in the head file and in the implementation file.建筑商签名(p)字母名称(P)And what I wrote in the implementation started with(p)字母名称(P)Thus I accidently replaced"pset"with"pest".The compiler was complaining about this one and two other constructors in which there was no mistake at all.I'm using G++version 4.9.1 under Ubuntu.and defining a virtual destructor in this derived class made no difference(it is defined in the base class).I would have never found this bug if I did't paste the constructors'bodies in the head file,thus defining them in-class.(p)