所以,我变得臭名昭着
未定义的引用'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::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
Alexandre Ha.. 386
该GCC FAQ上有一个条目:
解决方案是确保定义所有非纯的虚方法.请注意,即使声明为pure-virtual [class.dtor]/7,也必须定义析构函数.
FFS,为什么编译器没有检查并打印错误消息? (123认同)
显然,这只能由链接器而不是编译器发现. (18认同)
`nm -C CGameModule.o | grep CGameModule ::`将列出定义的方法,假设您的整个类实现进入逻辑对象文件.您可以将其与定义为虚拟的内容进行比较,以找出您错过的内容. (16认同)
小智.. 154
对于它的价值,忘记虚拟析构函数上的主体会生成以下内容:
未定义引用`vtable for CYourClass'.
我正在添加注释,因为错误消息具有欺骗性.(这是gcc版本4.6.3.)
该GCC FAQ上有一个条目:
解决方案是确保定义所有非纯的虚方法.请注意,即使声明为pure-virtual [class.dtor]/7,也必须定义析构函数.
对于它的价值,忘记虚拟析构函数上的主体会生成以下内容:
未定义引用`vtable for CYourClass'.
我正在添加注释,因为错误消息具有欺骗性.(这是gcc版本4.6.3.)
所以,我已经找到了问题,这是一个错误的逻辑组合,并没有完全熟悉automake/autotools世界.我正在将正确的文件添加到我的Makefile.am模板中,但我不确定构建过程中的哪一步实际创建了makefile本身.所以,我正在使用一个不知道我的新文件的旧makefile进行编译.
感谢您的回复和GCC常见问题解答的链接.我一定会读到这一点,以避免出现这个问题,原因很简单.
如果您使用的是Qt,请尝试重新运行qmake.如果此错误发生在窗口小部件的类中,则qmake可能未注意到应重新生成ui类vtable.这为我解决了这个问题.
由于以下情况,可能会发生对vtable的未定义引用.试试这个:
A类包含:
virtual void functionA(parameters)=0; virtual void functionB(parameters);
B类包含:
上述功能的定义A.
上述functionB的定义.
C类包含:现在您正在编写一个C类,您将从A类中派生它.
现在,如果您尝试编译,您将获得对C类的vtable的未定义引用作为错误.
原因:
functionA
定义为纯虚拟,其定义在类B中提供.
functionB
定义为虚拟(NOT PURE VIRTUAL),因此它尝试在类A中找到它的定义,但是您在类B中提供了它的定义.
解:
将函数B设为纯虚拟(如果你有这样的要求)
virtual void functionB(parameters) =0;
(这是有效的)
为A类中的functionB提供定义,使其保持为虚拟.(希望它有效,因为我没试过这个)
我只是得到了这个错误,因为我的cpp文件不在makefile中.
这里有各种各样的答案,有很多猜测.我将在下面给出一个相当少的代码来重现这个错误并解释它为什么会发生.
相当简单的代码重现此错误
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#include "Derived.hpp" class MyClass { public: MyClass(std::shared_ptr newInstance) : instance(newInstance) { } void doSomething() { instance->action(); } private: std::shared_ptr instance; }; int main(int argc, char** argv) { Derived myInstance(5); MyClass c(std::make_shared (myInstance)); c.doSomething(); return 0; }
您可以使用GCC编译它,如下所示:
g++ -std=c++11 -o a.out myclass.cpp Derived.cpp
您现在可以通过= 0
在IBase.hpp中删除来重现错误 .我收到此错误:
~/.../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的构造函数.但是,由于Derived会覆盖IBase中的方法,因此它附加了vtable,它将引用IBase.当链接器说"对IBase的vtable的未定义引用"时,它基本上意味着Derived具有对IBase的vtable引用,但它找不到任何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 int foo(int x = 0);
并且子类有
int foo(int x) override;
问题是错误"=0"
应该是在括号之外:
virtual int foo(int x) = 0;
所以,如果你滚动这么远,你可能没有找到答案 - 这是另外要检查的东西.
如果您忘记链接到具有该定义的目标文件,则很容易发生这种情况.
GNU C++编译器必须决定在何处放置vtable
以防止跨多个编译单元的对象的虚函数的定义(例如,一些对象虚拟函数定义在.cpp文件中,其他在另一个中. cpp文件,等等).
编译器选择将它放在与vtable
定义第一个声明的虚函数的位置相同的位置.
现在,如果由于某种原因忘记为对象中声明的第一个虚函数提供定义(或者错误地忘记在链接阶段添加编译对象),则会出现此错误.
作为一个副作用,请注意,只有这个特定的虚函数,你不会得到传统的链接器错误,就像你缺少函数foo.
不要过马路但是.如果你正在处理继承,第二次google点击是我错过的,即.应该定义所有虚拟方法.
如:
virtual void fooBar() = 0;
有关详细信息,请参阅answare C++未定义的vtable和继承参考.刚刚意识到它已经在上面提到了,但是它可能对某人有帮助.
好的,解决方法是您可能错过了定义.请参阅下面的示例以避免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 { }
你确定CDasherComponent
有一个析构函数的主体吗?它肯定不在这里 - 问题是它是否在.cc文件中.
从样式的角度来看,CDasherModule
应该明确定义它的析构函数virtual
.
看起来最后CGameModule
有一个额外}
的(之后}; // for the class
).
是CGameModule
被链接的是定义库CDasherModule
和CDasherComponent
?
也许缺少虚拟析构函数是影响因素?
virtual ~CDasherModule(){};
这是我的第一个搜索结果,所以我想我要添加另一个东西来检查:确保虚函数的定义实际上在类上.就我而言,我有这个:
头文件:
class A { public: virtual void foo() = 0; }; class B : public A { public: void foo() override; };
在我的.cc文件中:
void foo() { ... }
这应该是读
void B::foo() { }