未定义对vtable的引用


357

在构建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

哪个函数抛出“对vtable的未定义引用...”?
J. Polfer 2010年

3
我完全错过了错误消息指定一个函数的信息。碰巧是构造函数,所以我看到了我的类名,但没有建立连接。因此,构造函数将抛出此错误。我会将这些细节添加到我的原始帖子中。
RyanG 2010年

3
如果在进行重大更改(例如qmake -project再进行qmake)以生成new 之后仍未重建项目文件Makefile,则可能是使用Qt时错误的来源。
David C. Rankin

@ DavidC.Rankin,另一个与Qt相关的问题是,如果with的文件Q_OBJECT是从外部复制的,但还不是.pro文件的一部分,则尽管可以很好地编译,但它不会链接。我们必须将该.h/.cpp文件添加到.pro文件中才能执行qmake
iammilind

Answers:


420

GCC FAQ上有一个条目:

解决方案是确保定义了所有非纯虚拟方法。请注意,即使已将析构函数声明为纯虚拟[class.dtor] / 7,也必须对其进行定义。


17
nm -C CGameModule.o | grep CGameModule::假设您的整个类实现都进入逻辑对象文件,它将列出定义的方法。您可以将其与定义为虚拟的进行比较,以找出错过的内容。
Troy Daniels 2014年

132
FFS,为什么编译器不检查并打印错误消息?
Lenar Hoyt 2014年

20
显然,这只能由链接器而不是编译器发现。
Xoph

2
就我而言,我们有没有析构函数实现的抽象类。我不得不把空的实现
〜MyClass

1
当您尝试链接的对象在归档文件(libxyz.a文件)中丢失时,您会得到这样的错误:未定义引用'vtable for objfilename'–
Kemin Zhou

162

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

未定义对CYourClass的vtable的引用。

我正在添加一条注释,因为错误消息具有欺骗性。(这是在gcc版本4.6.3中使用的。)


23
我必须将空虚析构函数的主体明确地放在定义文件(* .cc)中。标头中仍然有错误。
PopcornKing

4
请注意,一旦将虚拟析构函数添加到实现文件中,gcc就会告诉我实际的错误,该错误是另一个函数的主体。
moodboom

1
@PopcornKing我看到了同样的问题。即使~Destructor = default;在头文件中定义也无济于事。是否有针对gcc的已记录错误?
RD 2015年

这可能是一个不同的问题,但是我的问题只是没有为非虚拟析构函数实现(切换到唯一/共享指针并将其从源文件中删除,但标头中没有“实现” )
svenevs,2016年

这解决了我的问题,为虚拟析构函数添加空的{}主体可以避免该错误。
Bogdan Ionitza

55

因此,我已经弄清了这个问题,这是一个糟糕的逻辑的结合,并且并不完全了解automake / autotools世界。我将正确的文件添加到我的Makefile.am模板中,但是我不确定构建过程中的哪一步实际创建了makefile本身。因此,我正在使用一个旧的makefile进行编译,而该文件根本不知道我的新文件。

感谢您的回复和指向GCC常见问题解答的链接。我一定会读到,以避免由于真正原因而发生此问题。


43
简而言之:.cpp只是不包含在构建中。该错误消息确实令人误解。
Offirmo

66
对于Qt用户:如果忘记了移动标头,则可能会遇到同样的错误。
克里斯·莫里尔

8
我认为您应该接受Alexandre Hamez的回答。寻找此错误的人们很可能需要他的解决方案,而不是您的解决方案。
蒂姆(Tim)

13
-1这可能是您的问题的解决方案,但它不是原始问题的答案。正确的答案很简单,就是您没有为目标文件提供必需的符号。为什么您未能提供他们是另一回事。
沃尔特

12
@沃尔特:实际上,这是我想要的确切答案。其他的很明显,因此毫无用处。
Edgar Bonet 2014年

50

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


2
我只是删除了整个文件夹,并使其正常工作。
托马什Zato -恢复莫妮卡

这不是唯一的qmake,我也有cmake。问题的一部分可能是这两个工具的头文件都有一些问题,它们在需要时可能并不总是触发重建。
MSalters

2
以为“ Rebuild”会自动重新运行qmake……显然不是。我按照您的建议执行了“ Run qmake”,然后执行了“ Rebuild”,它解决了我的问题。
yano

45

由于以下情况,可能还会出现对vtable的未定义引用。尝试一下:

A类包含:

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

B类包含:

  1. 以上功能的定义A。
  2. 上面函数B的定义。

C类包含:现在,您正在编写C类,将从A类派生该类。

现在,如果您尝试编译,您将获得对C类的vtable的未定义引用作为错误。

原因:

functionA 被定义为纯虚拟,并且其定义在类B中提供。 functionB被定义为虚拟(非纯虚拟),因此它尝试在类A本身中查找其定义,但是您在类B中提供了它的定义。

解:

  1. 将函数B设为纯虚函数(如果您有这样的要求) virtual void functionB(parameters) =0; (此方法已经过测试)
  2. 为类A本身中的functionB提供定义,使其保持为虚拟。(希望它能正常工作,因为我没有尝试过)

@ ilya1725您建议的编辑不仅是固定格式等,您还更改了答案,例如说C类是从B而不是A派生的,并且您正在更改第二个解决方案。这大大改变了答案。在这种情况下,请给作者留下评论。谢谢!
法比奥说莫妮卡(Monica)恢复职权是

@FabioTurati classC从此继承什么类?句子不清楚。另外,“ C类包含:”是什么意思?
ilya1725 '17

@ ilya1725这个答案不是很清楚,我也不反对对其进行编辑和改进。我的意思是您的编辑更改了答案的含义,而这太过分了。希望作者能够介入并阐明他的意思(尽管他已经很长时间没有活动了)。
法比奥称莫妮卡(Monica)恢复职权

谢谢!就我而言,层次结构中只有2个类。类A声明了纯虚拟方法。B类的声明表明它将覆盖此方法,但我尚未编写覆盖方法的定义。
Nick Desaulniers

44

我只是收到此错误,因为我的cpp文件不在makefile中。


确实,undefined reference to {function/class/struct}virtual涉及到某些情况时,该消息似乎与通常的消息有所不同。把我扔了。
基思M

30

什么是vtable

在尝试修复该错误消息之前,先了解该错误消息的含义可能很有用。我将从一个较高的层次开始,然后深入研究更多细节。这样,一旦人们对vtable的理解感到满意,他们便可以跳过。…现在有一堆人向前跳过。:)对于那些坚持的人:

vtable基本上是C ++中最常见的多态 实现。使用vtable时,每个多态类在程序中的某个位置都有一个vtable。您可以将其视为类的(隐藏)数据成员。多态类的每个对象都与其vtable关联,为其最派生的类。通过检查这种关联,程序可以发挥其多态魔力。重要警告: vtable是实现细节。尽管大多数(所有)C ++编译器都使用vtables实现多态行为,但C ++标准并没有强制要求使用它。我要介绍的细节是典型或合理的方法。允许编译器偏离这一点!static

每个多态对象都有一个(隐藏的)指向对象最派生类的vtable的指针(在更复杂的情况下,可能有多个指针)。通过查看指针,程序可以知道对象的“真实”类型是什么(构造期间除外,但让我们跳过这种特殊情况)。例如,如果类型的对象A未指向的vtable A,则该对象实际上是从派生的对象的子对象A

“虚函数表”这个名字来源于“ v irtual功能 ”。它是一个表,用于存储指向(虚拟)函数的指针。编译器为表的布局选择约定。一种简单的方法是按照在类定义中声明的顺序遍历虚拟函数。调用虚拟函数时,程序将跟随对象的指针指向vtable,转到与所需函数关联的条目,然后使用存储的函数指针来调用正确的函数。进行这项工作有很多技巧,但是我不会在这里讨论。

在哪里/何时vtable生成?

vtable由编译器自动生成(有时称为“发射”)。编译器可以在看到多态类定义的每个翻译单元中发出一个vtable,但这通常是不必要的。一种替代方法(由gcc以及其他人使用)是选择一个放置vtable的转换单元,类似于您选择放置一个类的静态数据成员的单个源文件的方式。如果此选择过程未能选择任何翻译单元,则vtable将成为未定义的引用。因此,该错误的信息尚不十分清楚。

类似地,如果选择过程确实选择了翻译单元,但是该目标文件没有提供给链接器,则vtable将成为未定义的引用。不幸的是,与选择过程失败的情况相比,在这种情况下错误消息的清晰度甚至更低。(感谢回答者提到了这种可能性。否则我可能会忘记了。)

如果我们从将(单个)源文件分配给每个需要一个实现类的类的传统开始,gcc使用的选择过程就很有意义。编译该源文件时发出vtable会很好。我们称之为目标。但是,即使不遵循这一传统,选择过程也需要进行。因此,与其寻找整个类的实现,不如寻找该类特定成员的实现。如果遵循传统- 如果该成员实际上得到执行 -那么就可以实现目标。

由gcc(可能还有其他编译器)选择的成员是第一个非纯内嵌虚拟函数,它不是纯虚函数。如果您是在其他成员函数之前声明构造函数和析构函数的人群中的一部分,则该析构函数很可能被选择。(您确实记得将析构函数虚拟化了,对吗?)有一些例外;例如:我希望最常见的例外是为析构函数提供内联定义时以及请求默认析构函数时(使用“ = default”)。

精明的人可能会注意到,允许多态类为其所有虚函数提供内联定义。这不是导致选择过程失败吗?在较旧的编译器中也是如此。我读到最新的编译器已经解决了这种情况,但是我不知道相关的版本号。我可以尝试查找,但是围绕它编码或等待编译器抱怨会更容易。

总之,导致“对vtable的引用未定义”错误的三个主要原因:

  1. 成员函数缺少其定义。
  2. 未链接目标文件。
  3. 所有虚拟函数都有内联定义。

这些原因本身不足以独自导致错误。而是,这些是您解决错误所要解决的问题。不要期望故意创建其中一种情况肯定会产生此错误;还有其他要求。请确保解决这些情况将解决此错误。

(好的,问这个问题时3号就足够了。)

如何解决错误?

欢迎后面的人跳过!:)

  1. 查看您的类定义。找到第一个非纯虚函数,它不是纯虚函数(不是“ = 0”),并且您要提供其定义(不是“ = default”)。
    • 如果没有这样的功能,请尝试修改您的类,以便有一个。(错误可能已解决。)
    • 另请参见Philip Thomas的答案
  2. 查找该函数的定义。如果丢失,请添加!(错误可能已解决。)
  3. 检查您的链接命令。如果它没有使用该函数的定义提及目标文件,请修复该问题!(错误可能已解决。)
  4. 对于每个虚拟功能,然后对每个非虚拟功能重复步骤2和3,直到错误解决。如果仍然遇到问题,请对每个静态数据成员重复此操作。

示例
做什么的详细信息可能会有所不同,有时会分为几个独立的问题(例如,什么是未定义的引用/未解决的外部符号错误,以及如何解决此错误?)。不过,我将提供一个示例,说明在特定情况下可能会使新程序员感到困惑的情况。

步骤1提到修改类,使其具有某种类型的功能。如果您对该功能的描述不屑一顾,则可能是我打算解决的情况。请记住,这是实现目标的一种方式。这不是唯一的方法,在您的特定情况下很容易会有更好的方法。让我们称您的班级A。是否在您的类定义中将析构函数声明为

virtual ~A() = default;

要么

virtual ~A() {}

?如果是这样,只需两个步骤即可将析构函数更改为我们想要的函数类型。首先,将该行更改为

virtual ~A();

其次,将以下行放入项目中的源文件中(如果有的话,最好是具有类实现的文件):

A::~A() {}

这使您的(虚拟)析构函数成为非内联的,并且不是由编译器生成的。(可以随意修改内容以更好地匹配您的代码格式样式,例如在函数定义中添加标题注释。)


太好了 对于非常详细且范围很广的解释。
David C. Rankin

只好向下滚动到远处才能阅读此内容。有一个很好的解释投票!
托马斯

24

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

重现此错误的代码极少

iBase.hpp

#pragma once

class IBase {
    public:
        virtual void action() = 0;
};

派生文件

#pragma once

#include "IBase.hpp"

class Derived : public IBase {
    public:
        Derived(int a);
        void action() override;
};

派生文件

#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

现在,您可以通过= 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的构造函数,将需要它。但是,由于“派生”会覆盖IBase的方法,因此它附带了将引用IBase的vtable。当链接器说“对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指定为库。


18

我只是遇到了另一个您可以检查的错误原因。

基类将纯虚函数定义为:

virtual int foo(int x = 0);

子类有

int foo(int x) override;

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

virtual int foo(int x) = 0;

因此,如果您将其向下滚动很远,则可能找不到答案-这是要检查的其他内容。


12

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


1
请为您的答案和可能的解决方法添加更多说明。
Mohit Jain

1
忘记链接可以包括忘记添加构建说明。就我而言,我的cpp文件具有完美的“定义”,只是我忘记将cpp文件添加到源列表(在我的CMakeLists.txt中,但是在其他构建系统中也可能发生这种情况,例如.pro文件中)。结果,所有东西都编译好了,然后在链接时我得到了错误……
sage

@Mohit Jain如何链接目标文件取决于环境设置和工具。不幸的是,针对一个人的特定修复方法可能与另一个人不同(例如CMake,专有工具,IDE和其他)
Hazok

11

vtable如果您具有跨多个编译单元分布的对象的虚函数定义(例如,某些对象的虚函数定义位于.cpp文件中,而另一些则位于.cpp文件中),则GNU C ++编译器必须决定放置在何处。 cpp文件等)。

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

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

作为副作用,请注意,仅对于此特定的虚函数,不会像丢失缺少的foo那样得到传统的链接器错误。



8

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

}

7
  • 您确定其中CDasherComponent有一个销毁人员的尸体吗?绝对不在这里-问题是它是否在.cc文件中。
  • 从样式角度来看,CDasherModule应显式定义其析构函数virtual
  • 看起来 在结尾处CGameModule有一个多余}的字符(之后}; // for the class)。
  • 是否CGameModule与定义CDasherModule和的库链接CDasherComponent

-是的,CDasherComponent在cpp中具有析构函数主体。我以为它是在发布时在.h中声明的。-充分注意。-这是我在剥离文档时误加的一个括号。-据我了解,是的。我一直在修改一个我没有写的automake文件,但是我一直在遵循适用于其他类的模式,这些类具有来自相同类的相同继承模式,因此除非我犯了一个愚蠢的错误(完全可能) ,我认为不是这样。
RyanG 2010年

@RyanG:尝试将所有虚函数定义移到类定义中。确保他们都在那儿,看看结果是否有所变化。
斯蒂芬


5

这是我的第一个搜索结果,因此我认为我还要检查另一件事:确保虚拟函数的定义确实在类中。就我而言,我有这个:

头文件:

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

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

在我的.cc文件中:

void foo() {
  ...
}

这应该读

void B::foo() {
}


3

这里有这么多答案,但似乎没有一个涵盖我的问题。我有以下内容:


class I {
    virtual void Foo()=0;
};

并且在另一个文件中(当然包括在编译和链接中)

class C : public I{
    void Foo() {
        //bar
    }
};

好吧,这没有用,我得到了每个人都在谈论的错误。为了解决这个问题,我不得不将Foo的实际定义移出类声明,例如:

class C : public I{
    void Foo();
};

C::Foo(){
   //bar
}

我不是C ++专家,所以我无法解释为什么这更正确,但却为我解决了问题。


我不是C ++专家,但它似乎与将声明和定义混合在同一文件中以及其他定义文件有关。
Terry G Lorber

1
当函数定义在类定义中时,它被隐式声明为“内联”。到那时,所有虚拟函数都已内联。当您将函数移到类定义之外时,它不再是“内联”函数。由于您具有非内联虚拟函数,因此编译器知道在何处发出vtable。(更详尽的解释将不适合发表评论。)
JaMiT

3

所以我在Windows XP和MinGW编译器上使用Qt,这使我发疯。

基本上,即使添加了moc_xxx.cpp也是空的

Q_OBJECT

删除使函数虚拟化,显式化的所有内容,以及您认为不起作用的所有内容。最后,我开始逐行删除,结果发现我有

#ifdef something

围绕文件。即使#ifdef为true,也不会生成moc文件。

因此,删除所有#ifdefs可以解决此问题。

Windows和VS 2013不会发生这种情况。


注释掉Q_OBJECT行使我的简单测试应用程序的构建变得简单g++ *.cpp ...。(需要一些快速又脏的东西,但qmake充满了悲伤。)
内森·基德

2

如果其他所有方法均失败,请查找重复项。在构造函数和析构函数的显式初始引用之前,我被误导了,直到在另一篇文章中阅读了该引用。这是任何未解决的方法。就我而言,我以为我已经使用了一个不必要的麻烦的const char * xml替换了使用char * xml作为参数的声明,但是,我创建了一个新的并将另一个保留在原处。


2

提到了很多导致此错误的可能性,我敢肯定其中有许多确实会导致该错误。在我的情况下,由于源文件的重复,因此存在相同类的另一个定义。该文件已编译,但未链接,因此链接器抱怨无法找到它。

总而言之,我想说的是,如果您盯着该类学习了足够长的时间,并且看不到可能引起语法错误的原因,请查找诸如丢失文件或重复文件之类的构建问题。



2

我认为还值得一提的是,当您尝试链接到具有至少一个虚拟方法且链接器找不到该文件的任何类的对象时,也会收到该消息。例如:

Foo.hpp:

class Foo
{
public:
    virtual void StartFooing();
};

Foo.cpp:

#include "Foo.hpp"

void Foo::StartFooing(){ //fooing }

编译:

g++ Foo.cpp -c

和main.cpp:

#include "Foo.hpp"

int main()
{
    Foo foo;
}

编译并链接到:

g++ main.cpp -o main

给出我们最喜欢的错误:

/tmp/cclKnW0g.o:在函数main': main.cpp:(.text+0x1a): undefined reference tovtable中执行Foo'collect2:错误:ld返回1退出状态

这是由于我不为人所知的缘故:

  1. 在编译时按类创建Vtable

  2. 链接器无权访问Foo.o中的vtable


1

在以下情况下出现此错误

考虑一种情况,您已经在头文件本身中定义了类的成员函数的实现。该头文件是导出的头(换句话说,可以将其直接复制到代码库中的一些common / include中)。现在,您已经决定将成员函数的实现分离到.cpp文件中。在将实现分离/移动到.cpp之后,头文件现在仅具有类内部成员函数的原型。完成上述更改后,如果您构建代码库,则可能会收到“未定义对'vtable ...的引用”错误。

要解决此问题,请在构建之前确保删除common / include目录中的头文件(对其进行了更改)。另外,还要确保将makefile更改为适应/添加从刚创建的新.cpp文件构建的新.o文件。当您执行这些步骤时,编译器/链接器将不再抱怨。


奇怪。如果要在其他位置复制标头,则构建系统应在原始文档被修改之后以及在任何其他文件包含之前自动更新副本。如果您必须手动执行操作,则会被拧紧。
Offirmo

1

当我遇到一个make bug导致无法将对象添加到存档中时,在尝试链接到对象的情况下出现了此类错误。

说我有libXYZ.a,它应该在int中有bioseq.o,但没有。

我收到一个错误:

combineseq.cpp:(.text+0xabc): undefined reference to `vtable for bioseq'

这与以上所有都不相同。我会在存档问题中称这个丢失的对象。


0

您也可能会收到类似

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类的虚函数。

//class declaration in class1.h
class class1
{
    public:
    class1()
    {
    }
    virtual ~class1()
    {
    }
    virtual void ForgottenFunc();
};

//class definition in FakeClass1.h
//...
//void ForgottenFunc() {} is missing here

在这种情况下,我建议您再次检查您的class1假货。您可能会发现您可能忘记了ForgottenFunc在假类中定义虚函数。


0

当我在现有的源/标题对中添加第二个类时,出现此错误。同一.h文件中的两个类头,以及同一.cpp文件中的两个类的函数定义。

我之前已经成功完成了这些工作,而这些课本应该紧密协作,但是显然这次有些不喜欢我的事情了。仍然不知道什么,但是每个编译单元将它们拆分为一个类就可以修复它。


失败的尝试:

_gui_icondata.h:

#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_icondata.cpp:

#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类剪切/粘贴到“刚刚工作”。


0

我的案子很愚蠢,误入歧途"后又#include猜出什么呢?

undefined reference to vtable!

数小时以来,我一直在抓挠我的头部和脸部,评论虚拟功能以查看是否发生了任何变化,最后删除了多余的部分 "东西,一切修复!这类事情确实需要导致编译错误而不是链接错误。

另外",我的意思是:

#include "SomeHeader.h""

0

就我而言,我有一个名为Person的基类和两个名为Student和Professor的派生类。

我的程序是如何修复的,1.我在基类中完成了所有功能Pure Virtual. 。2.我将所有虚拟析构函数用作default ones.


-3

我收到此错误仅仅是因为头文件和实现文件中构造函数参数的名称不同。构造函数签名为

PointSet (const PointSet & pset, Parent * parent = 0);

我在实现中写的内容始于

PointSet (const PointSet & pest, Parent * parent)

因此我不小心将“ pset”替换为“害虫”。编译器抱怨这一个和另外两个构造函数完全没有错误。我正在Ubuntu下使用g ++ 4.9.1版本。在此派生类中定义虚拟析构函数没有区别(它在基类中定义)。如果不将构造函数的主体粘贴到头文件中,从而在类中对其进行定义,我将永远不会发现此错误。


7
那根本没有什么区别,您必须在其他地方有错误并且无意中将其修复。
MM
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.