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

当前回答

GNU c++编译器必须决定将虚表放在哪里,以防对象的虚函数定义分布在多个编译单元中(例如,一些对象的虚函数定义在一个.cpp文件中,另一些在另一个.cpp文件中,等等)。

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

现在,如果由于某种原因忘记为对象中声明的第一个虚函数提供定义(或者错误地忘记在链接阶段添加已编译的对象),您将得到这个错误。

作为一个副作用,请注意,只有对于这个特定的虚函数,你才不会得到传统的链接错误,比如你缺少函数foo。

其他回答

所以,我已经找到了问题所在,这是一个糟糕的逻辑和不完全熟悉automake/autotools世界的组合。我正在向Makefile中添加正确的文件。am模板,但我不确定在我们的构建过程中哪一步实际上创建了makefile本身。所以,我用一个旧的makefile编译,它对我的新文件一无所知。

感谢您的回复以及GCC FAQ的链接。我一定会阅读它,以避免这个问题出现真正的原因。

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

重现此错误的代码相当少

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

现在可以通过在hbase .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

解释

请注意,上面的代码不需要任何虚拟析构函数、构造函数或任何其他额外的文件才能成功编译(尽管您应该拥有它们)。

The way to understand this error is as follows: Linker is looking for constructor of IBase. This it will need it for the constructor of Derived. However as Derived overrides methods from IBase, it has vtable attached to it that will reference IBase. When linker says "undefined reference to vtable for IBase" it basically means that Derived has vtable reference to IBase but it can't find any compiled object code of IBase to look up to. So the bottom line is that class IBase has declarations without implementations. This means a method in IBase is declared as virtual but we forgot to mark it as pure virtual OR provide its definition.

分小费

如果所有这些都失败了,那么调试这个错误的一种方法是构建最小的程序,它可以编译,然后不断更改它,使它达到您想要的状态。在此期间,继续编译以查看它何时开始失败。

注意ROS和柳絮构建系统

如果你在ROS中使用catkin构建系统编译上述一组类,那么你将需要在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指定为库。

在我的情况下,我使用Qt,并在foo.cpp(不是.h)文件中定义了一个QObject子类。修正是添加#include "foo。Moc "在foo.cpp的末尾。

FWIW我能够避免以下错误:

 ld: /usr/local/lib/libvmaf.a(svm.cpp.o):(.data.rel.ro._ZTI7QMatrix[_ZTI7QMatrix]+0x0): undefined reference to `vtable for __cxxabiv1::__class_type_info'

当将“C”项目与“c++”项目中的静态库链接时,通过在链接参数中添加-lstdc++。它是gcc -lstdc++,现在可以了。

最常见的方法是将-lstdc++添加到库的pkgconfig .pc文件库列表中。或者改用g++链接。

GCC FAQ中有一个条目:

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

因此,你需要为虚析构函数提供一个定义:

virtual ~CDasherModule()
{ }