当构建我的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

当前回答

我尝试了JaMIT的所有详细步骤,仍然被这个错误难住了。经过一番反复,我终于明白了。我太粗心了。您应该能够使用以下示例代码重现这个令人痛苦的错误。

[jaswantp@jaswant-arch build]$ gcc -v
Using built-in specs.
COLLECT_GCC=gcc
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/lto-wrapper
Target: x86_64-pc-linux-gnu
Configured with: /build/gcc/src/gcc/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++,d --with-isl --with-linker-hash-style=gnu --with-system-zlib --enable-__cxa_atexit --enable-cet=auto --enable-checking=release --enable-clocale=gnu --enable-default-pie --enable-default-ssp --enable-gnu-indirect-function --enable-gnu-unique-object --enable-install-libiberty --enable-linker-build-id --enable-lto --enable-multilib --enable-plugin --enable-shared --enable-threads=posix --disable-libssp --disable-libstdcxx-pch --disable-libunwind-exceptions --disable-werror gdc_include_dir=/usr/include/dlang/gdc
Thread model: posix
Supported LTO compression algorithms: zlib zstd
gcc version 10.2.0 (GCC) 

// CelesetialBody.h
class CelestialBody{
public:
    virtual void Print();
protected:
    CelestialBody();
    virtual ~CelestialBody();
};
// CelestialBody.cpp
#include "CelestialBody.h"

CelestialBody::CelestialBody() {}

CelestialBody::~CelestialBody() = default;

void CelestialBody::Print() {}
// Planet.h
#include "CelestialBody.h"

class Planet : public CelestialBody
{
public:
    void Print() override;
protected:
    Planet();
    ~Planet() override;
};
// Planet.cpp
#include "Planet.h"

Planet::Planet() {}
Planet::~Planet() {}

void Print() {} // Deliberately forgot to prefix `Planet::`
# CMakeLists.txt
cmake_minimum_required(VERSION 3.12)
project (space_engine)
add_library (CelestialBody SHARED CelestialBody.cpp)
add_library (Planet SHARED Planet.cpp)
target_include_directories (CelestialBody PRIVATE ${CMAKE_CURRENT_LIST_DIR})  
target_include_directories (Planet PRIVATE ${CMAKE_CURRENT_LIST_DIR})    
target_link_libraries (Planet PUBLIC CelestialBody)

# hardened linker flags to catch undefined symbols
target_link_options(Planet 
    PRIVATE 
    -Wl,--as-needed
    -Wl,--no-undefined
)

我们得到了我们最喜欢的误差。

$ mkdir build
$ cd build
$ cmake ..
$ make
[ 50%] Built target CelestialBody
Scanning dependencies of target Planet
[ 75%] Building CXX object CMakeFiles/Planet.dir/Planet.cpp.o
[100%] Linking CXX shared library libPlanet.so
/usr/bin/ld: CMakeFiles/Planet.dir/Planet.cpp.o: in function `Planet::Planet()':
Planet.cpp:(.text+0x1b): undefined reference to `vtable for Planet'
/usr/bin/ld: CMakeFiles/Planet.dir/Planet.cpp.o: in function `Planet::~Planet()':
Planet.cpp:(.text+0x3d): undefined reference to `vtable for Planet'
collect2: error: ld returned 1 exit status
make[2]: *** [CMakeFiles/Planet.dir/build.make:104: libPlanet.so] Error 1
make[1]: *** [CMakeFiles/Makefile2:97: CMakeFiles/Planet.dir/all] Error 2
make: *** [Makefile:103: all] Error 2

我在Planet.cpp中所做的事情当然应该通过这个技巧来解决

查看类定义。找到第一个非纯虚函数(不是“= 0”),并且您提供了其定义(不是“= default”)的非内联虚函数。

JaMIT的回答。 如果有其他人尝试了以上所有方法,但没有任何效果,可能您也像我一样,不小心忘记给一个或多个成员函数加上<ClassName>::前缀。

要么我需要检查一下眼睛,要么我需要睡一会儿。

其他回答

对vtable的未定义引用也可能由于以下情况而发生。试试这个:

A类包含:

virtual void functionA(parameters)=0; 
virtual void functionB(parameters);

B类包含:

上述函数a的定义。 上述函数b的定义。

类C包含:现在你正在编写一个类C,你将从类a派生它。

现在如果你尝试编译,你会得到未定义的引用vtable类C作为错误。

原因:

函数a被定义为纯虚函数,其定义在类B中提供。 函数B被定义为虚(不是纯虚),所以它试图在类A中找到它的定义,但你在类B中提供了它的定义。

解决方案:

使函数B为纯虚函数(如果你有这样的要求) 虚空函数b(参数)=0; (这是经过测试的) 在类A中为函数b提供定义,保持它为虚的。 (希望它能起作用,因为我没有尝试过这个)

你确定CDasherComponent有一个析构函数体吗?它肯定不在这里——问题是它是否在.cc文件中。 从样式的角度来看,CDasherModule应该显式地定义它的析构函数虚函数。 看起来CGameModule在末尾有一个额外的}(在}之后;//类)。 CGameModule是否被链接到定义CDasherModule和CDasherComponent的库中?

这是GCC中的一个错误特性。也就是说,g++编译器本身不能抱怨未定义的虚方法,因为它们可以在其他地方定义。但是-它不存储关于缺少哪些虚拟成员的信息;它只存储了一个und定义的虚表符号,链接器会抱怨。

相反,如果要列出缺少的成员,则链接器可以告诉您它们是什么。

关于这个问题,GCC有一个公开的bug: bug 42540。不幸的是,它已经13岁了。

不管怎样,在虚析构函数上忘记主体会生成以下结果:

未定义的引用'虚表为CYourClass'。

我添加一个说明,因为错误消息是欺骗性的。(这是gcc 4.6.3版。)

如果所有这些都失败了,那就寻找副本。我被构造函数和析构函数的显式初始引用误导了,直到我在另一篇文章中读到引用。它是任何未解的方法。在我的例子中,我以为我已经将使用char *xml作为参数的声明替换为使用不必要的麻烦的const char *xml的声明,但相反,我创建了一个新的声明,并将另一个声明保留在原处。