关于C++:对vtable的未定义引用

Undefined reference to vtable

所以,我变得非常可怕

undefined reference to 'vtable...

以下代码出错(相关类是cgamemodule),我一辈子都无法理解问题是什么。起初,我以为这和忘记给一个虚拟的身体功能有关,但据我所知,一切都在这里。继承链有点长,但这里是相关的源代码。我不知道该提供什么其他信息。

注意:看起来,构造函数就是这个错误发生的地方。

我的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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;
};

继承自…

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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;
};

继承自……

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
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


GCC常见问题有一个条目:

The solution is to ensure that all virtual methods that are not pure are defined. Note that a destructor must be defined even if it is declared pure-virtual [class.dtor]/7.


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

undefined reference to `vtable for CYourClass'.

我添加了一个注释,因为错误消息具有欺骗性。(这是GCC 4.6.3版。)


所以,我已经解决了这个问题,它结合了糟糕的逻辑,不完全熟悉汽车制造/自动工具的世界。我正在将正确的文件添加到makefile.am模板中,但我不确定构建过程中的哪个步骤实际创建了makefile本身。所以,我正在编译一个旧的makefile,它对我的新文件一无所知。

感谢您的回复和GCC常见问题的链接。我一定会读到这篇文章,以避免这个问题的发生是有真正原因的。


(P)如果你使用QT,try rerunning qmake。如果这一错误是在Widget's class中,QMake might have failed to notice that the UI class vtable should be registed.这是我的问题。(p)


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

A级包含:

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

B类包含:

  • 上述功能的定义。
  • 上述函数b的定义。
  • 类C包含:现在您正在编写一个类C,在其中您将从类A派生它。

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

    原因:

    functionA定义为纯虚拟,其定义在B类中提供。functionB被定义为虚拟的(不是纯虚拟的),因此它试图在类A中找到它的定义,但您在类B中提供了它的定义。

    解决方案:

  • 使函数B成为纯虚拟的(如果您有这样的需求)virtual void functionB(parameters) =0;(这项工作是经过测试的)
  • 为类A中的函数b提供定义,使其保持为虚拟的。(希望它起作用,因为我没试过这个)

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


    (P)我只是因为另一个原因这一错误你可以检查。(p)(P)基本分类将虚拟函数定义为:(p)字母名称(P)And the subclass had(p)字母名称(P)The problem was the typo that the EDOCX1 genital 3 sil was supposed to be outside of the parents:(p)字母名称(P)So,in case you're scrolling this far down,you probably did't find the answer-this is some else to check for.(p)


    各种各样的答案中都有很多猜测。下面我将给出一个复制此错误的非常小的代码,并解释为什么会发生此错误。

    复制此错误的代码非常少

    Ibas.HPP

    1
    2
    3
    4
    5
    6
    #pragma once

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

    推导出

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #pragma once

    #include"IBase.hpp"

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

    派生CPP

    1
    2
    3
    #include"Derived.hpp"
    Derived::Derived(int a) { }
    void Derived::action() {}

    MyCase.CPP

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    #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如下编译:

    1
    g++ -std=c++11 -o a.out myclass.cpp Derived.cpp

    现在您可以通过删除ibase.hpp中的= 0来重现错误。我得到这个错误:

    1
    2
    3
    4
    5
    6
    7
    ~/.../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的构造函数。这将需要它作为派生的构造函数。但是,由于从IBase派生的重写方法,它附加了将引用IBase的vtable。当链接器说"未定义IBase对vtable的引用"时,它基本上意味着派生的具有对ibase的vtable引用,但找不到任何要查找的ibase编译对象代码。因此,底线是类IBase有没有实现的声明。这意味着IBase中的一个方法被声明为虚方法,但我们忘记了将其标记为纯虚方法或提供其定义。

    分型头

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

    关于ROS和Catkin构建系统的说明

    如果您使用catkin构建系统在ROS中编译上述一组类,那么您将需要在cmakelists.txt中使用以下行:

    1
    2
    3
    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指定为库。


    如果忘记链接到具有定义的对象文件,则很容易发生这种情况。


    (P)The GNU C++compiler has to make a decision where to put the EDOCX1 original 1 in case you have the definition of the virtual functions of an object spread across multiple compilations units(e.g.some of the objects vital functions definitions are in a CPP file,and so on).(p)(P)The compiler chooses to put the EDOCX1 universal 1 in the same place as the first declared virtual function is defined.(p)(P)Now if you for some reason forgot to provide a definition for that first vital function declared in the object(or mistakenly forgot to add the compiled object at linking phase),you will get this mistake.(p)(P)作为一个侧面效应,请注意,只有为这一特定的虚拟功能,你才不会得到传统的链接错误,就像你失去了功能。(p)


    不是为了越界,而是。如果您要处理继承问题,那么第二次谷歌命中是我错过的,也就是说,应该定义所有虚拟方法。

    例如:

    1
    virtual void fooBar() = 0;

    有关详细信息,请参见CaseSc++对VTABLE和继承的未定义引用。刚刚意识到上面已经提到了,但见鬼,它可能会帮助某人。


    好的,解决这个问题的方法是您可能遗漏了定义。请参阅下面的示例以避免vtable编译器错误:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    // 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是否与定义CDasherModuleCDasherComponent的库相关联?


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

    头文件:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class A {
     public:
      virtual void foo() = 0;
    };

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

    在我的.cc文件中:

    1
    2
    3
    void foo() {
      ...
    }

    这应该是

    1
    2
    void B::foo() {
    }


    (P)Perhaps missing the virtual destructor is contributing factor?(p)字母名称


    所以我在WindowsXP和Mingw编译器上使用qt,这让我抓狂。

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

    Q-对象

    删除所有使函数虚拟化、显式和任何你认为不起作用的东西。最后我开始一行一行地删除,结果发现我

    1
    #ifdef something

    在文件周围。即使ifdef是真的,也不会生成moc文件。

    所以移除所有的ifdef就解决了这个问题。

    这件事在Windows和VS 2013中没有发生。


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

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

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

    1
    2
    3
    4
    5
    class C : public I{
        void Foo() {
            //bar
        }
    };

    好吧,这不起作用,我得到了每个人都在说的错误。为了解决这个问题,我必须将foo的实际定义从类声明中移出,如下所示:

    1
    2
    3
    4
    5
    6
    7
    class C : public I{
        void Foo();
    };

    C::Foo(){
       //bar
    }

    我不是C++大师,所以我不能解释为什么这是更正确的,但它解决了我的问题。


    (P)不是Perhaps.你说的是什么?(p)


    (P)There are a lot of possibilities mentioned for causing this mistake,and I'm sure many of the m do cause the mistake.In my case,there was another definition of the same class,due to a duplication of the source file.This file was compiled,but not linked,so the linker was complaining about being unable to find it.(p)(P)To summarize,I would say that if you've started a t the class long enough and can't see what possible syntax problem could be causing i t,look for build issues like a missing file or a duplicated file.(p)


    在我的例子中,我使用qt并在foo.cpp文件(而不是.h文件)中定义了QObject子类。修复方法是在foo.cpp的末尾添加#include"foo.moc"


    如果其他都失败了,寻找重复。我被对构造函数和析构函数的明确初始引用误导了,直到我在另一篇文章中读到了一个引用。这是任何未解决的方法。在我的例子中,我认为我已经用一个不必要的麻烦的constchar*xml替换了使用char*xml作为参数的声明,但是我创建了一个新的声明,并将另一个声明留在原处。


    在我试图链接到一个对象的情况下,我得到了这种类型的错误,当我得到一个make bug时,它阻止了对象被添加到存档中。

    假设我有libxyz.a,它应该在int中有bioseq.o,但它没有。

    我有一个错误:

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

    这与上述所有情况不同。我会把这个丢失的对象称为归档问题。


    我在下面的场景中得到了这个错误

    考虑这样一种情况,您已经在头文件本身中定义了类的成员函数的实现。这个头文件是一个导出的头文件(换句话说,它可能被复制到代码库中的一些公共/包含中)。现在您决定将成员函数的实现分离到.cpp文件。将实现分离/移动到.cpp后,头文件现在只具有类内成员函数的原型。在上面的更改之后,如果您构建了代码库,您可能会得到"未定义的对‘vtable…’的引用"错误。

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


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

    Foo.hpp:

    1
    2
    3
    4
    5
    class Foo
    {
    public:
        virtual void StartFooing();
    };

    Foo.cpp:

    1
    2
    3
    #include"Foo.hpp"

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

    编译:

    1
    g++ Foo.cpp -c

    CPP:

    1
    2
    3
    4
    5
    6
    #include"Foo.hpp"

    int main()
    {
        Foo foo;
    }

    编译并链接到:

    1
    g++ main.cpp -o main

    给出了我们最喜欢的错误:

    /tmp/cclKnW0g.o: In function main': main.cpp:(.text+0x1a): undefined
    reference to
    vtable for Foo' collect2: error: ld returned 1 exit
    status

    这是我不为人知的秘密,因为:

  • vtable在编译时按类创建

  • 链接器无法访问foo.o中的vtable。


  • 你也有可能收到这样的信息

    1
    2
    3
    4
    5
    6
    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的虚拟函数。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    //class declaration in class1.h
    class class1
    {
        public:
        class1()
        {
        }
        virtual ~class1()
        {
        }
        virtual void ForgottenFunc();
    };

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

    在这种情况下,我建议你再检查一下你的1级假货。您可能会发现您可能忘记了在您的伪类中定义一个虚拟函数ForgottenFunc


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

    我以前已经成功地完成了这项工作,课程本来是要紧密合作的,但显然这次有些事情不喜欢我。仍然不知道是什么,但是把它们分成每个编译单元一个类就可以解决这个问题。

    失败的尝试:

    _ gui ou iconda.h.图:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    #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_iconda.cpp:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    #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类逐字剪切/粘贴到其中"刚刚工作"。


    (P)I got this mistake just because the name of a constructor argument differed in the head file and in the implementation file.建筑商签名(p)字母名称(P)And what I wrote in the implementation started with(p)字母名称(P)Thus I accidently replaced"pset"with"pest".The compiler was complaining about this one and two other constructors in which there was no mistake at all.I'm using G++version 4.9.1 under Ubuntu.and defining a virtual destructor in this derived class made no difference(it is defined in the base class).I would have never found this bug if I did't paste the constructors'bodies in the head file,thus defining them in-class.(p)