当前位置:  开发笔记 > 编程语言 > 正文

未定义的vtable参考

如何解决《未定义的vtable参考》经验,为你挑选了15个好方法。

所以,我变得臭名昭着

未定义的引用'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.)



1> Alexandre Ha..:

该GCC FAQ上有一个条目:

解决方案是确保定义所有非纯的虚方法.请注意,即使声明为pure-virtual [class.dtor]/7,也必须定义析构函数.


FFS,为什么编译器没有检查并打印错误消息?
显然,这只能由链接器而不是编译器发现.
`nm -C CGameModule.o | grep CGameModule ::`将列出定义的方法,假设您的整个类实现进入逻辑对象文件.您可以将其与定义为虚拟的内容进行比较,以找出您错过的内容.

2> 小智..:

对于它的价值,忘记虚拟析构函数上的主体会生成以下内容:

未定义引用`vtable for CYourClass'.

我正在添加注释,因为错误消息具有欺骗性.(这是gcc版本4.6.3.)


我必须明确地将我的空虚拟析构函数的主体放在定义文件(*.cc)中.将它放在标题中仍然给了我错误.
请注意,一旦我将虚拟析构函数添加到实现文件中,然后gcc告诉我实际错误,这是另一个函数上缺少的主体.

3> RyanG..:

所以,我已经找到了问题,这是一个错误的逻辑组合,并没有完全熟悉automake/autotools世界.我正在将正确的文件添加到我的Makefile.am模板中,但我不确定构建过程中的哪一步实际创建了makefile本身.所以,我正在使用一个不知道我的新文件的旧makefile进行编译.

感谢您的回复和GCC常见问题解答的链接.我一定会读到这一点,以避免出现这个问题,原因很简单.


对于Qt用户:如果忘记复制标题,则可能会出现同样的错误.
简而言之:.cpp刚刚未包含在构建中.错误消息确实具有误导性.
-1这可能是您问题的解决方案,但它不是原始问题的答案.正确答案就是您没有提供带有所需符号的目标文件.为什么你没有提供它们是另一个故事.
@Walter:实际上这是我正在寻找的确切答案.其他是显而易见的,因而无益.
我想你应该接受Alexandre Hamez的答案.搜索此错误的人很可能需要他的解决方案而不是您的解决方案.
我同意Edgar的观点,如果你错过了一个虚函数定义,你可能会遇到这样的错误,但有时,真正的答案确实是真正的答案.

4> gluk47..:

如果您使用的是Qt,请尝试重新运行qmake.如果此错误发生在窗口小部件的类中,则qmake可能未注意到应重新生成ui类vtable.这为我解决了这个问题.


我刚刚删除了整个文件夹,它也有效.
以为“ Rebuild”会自动重新运行qmake……显然不是。我按照您的建议执行了“ Run qmake”,然后执行了“ Rebuild”,它解决了我的问题。

5> 小智..:

由于以下情况,可能会发生对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提供定义,使其保持为虚拟.(希望它有效,因为我没试过这个)



6> Will..:

我只是得到了这个错误,因为我的cpp文件不在makefile中.



7> Shital Shah..:

这里有各种各样的答案,有很多猜测.我将在下面给出一个相当少的代码来重现这个错误并解释它为什么会发生.

相当简单的代码重现此错误

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指定为库.



8> 小智..:

我刚刚遇到了你可以检查的这个错误的另一个原因.

基类将纯虚函数定义为:

virtual int foo(int x = 0);

并且子类有

int foo(int x) override;

问题是错误"=0"应该是在括号之外:

virtual int foo(int x) = 0;

所以,如果你滚动这么远,你可能没有找到答案 - 这是另外要检查的东西.



9> Hazok..:

如果您忘记链接到具有该定义的目标文件,则很容易发生这种情况.



10> 小智..:

GNU C++编译器必须决定在何处放置vtable以防止跨多个编译单元的对象的虚函数的定义(例如,一些对象虚拟函数定义在.cpp文件中,其他在另一个中. cpp文件,等等).

编译器选择将它放在与vtable定义第一个声明的虚函数的位置相同的位置.

现在,如果由于某种原因忘记为对象中声明的第一个虚函数提供定义(或者错误地忘记在链接阶段添加编译对象),则会出现此错误.

作为一个副作用,请注意,只有这个特定的虚函数,你不会得到传统的链接器错误,就像你缺少函数foo.



11> Victor Häggq..:

不要过马路但是.如果你正在处理继承,第二次google点击是我错过的,即.应该定义所有虚拟方法.

如:

virtual void fooBar() = 0;

有关详细信息,请参阅answare C++未定义的vtable和继承参考.刚刚意识到它已经在上面提到了,但是它可能对某人有帮助.



12> 小智..:

好的,解决方法是您可能错过了定义.请参阅下面的示例以避免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
{

}



13> Stephen..:

你确定CDasherComponent有一个析构函数的主体吗?它肯定不在这里 - 问题是它是否在.cc文件中.

从样式的角度来看,CDasherModule应该明确定义它的析构函数virtual.

看起来最后CGameModule有一个额外} 的(之后}; // for the class).

CGameModule被链接的是定义库CDasherModuleCDasherComponent



14> DevByStarlig..:

也许缺少虚拟析构函数是影响因素?

virtual ~CDasherModule(){};



15> RedSpikeyThi..:

这是我的第一个搜索结果,所以我想我要添加另一个东西来检查:确保虚函数的定义实际上在类上.就我而言,我有这个:

头文件:

class A {
 public:
  virtual void foo() = 0;
};

class B : public A {
 public:
  void foo() override;
};

在我的.cc文件中:

void foo() {
  ...
}

这应该是读

void B::foo() {
}

推荐阅读
黄晓敏3023
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有