8wDlpd.png
8wDFp9.png
8wDEOx.png
8wDMfH.png
8wDKte.png

未定义对 vtable 的引用

AlgorithmAce 2月前

145 0

在构建 C++ 程序时,我收到错误消息“未定义对‘vtable 的引用...”此问题的原因是什么?我该如何修复它?我恰好收到以下错误:

在构建 C++ 程序时,我收到错误消息

对“vtable…”未定义引用

导致此问题的原因是什么?我该如何修复它?


碰巧的是,我遇到了以下代码的错误(有问题的类是 CGameModule。)我无论如何也想不出问题出在哪里。起初,我认为这与忘记给虚拟函数提供主体有关,但据我所知,一切都在这里。继承链有点长,但这是相关的源代码。我不确定我还应该提供什么其他信息。

注意:看起来,这个错误是在构造函数中发生。

我的代码:

游戏模块

class CGameModule : public Dasher::CDasherComponent {
 public:
  CGameModule() : CDasherModule() {};
  virtual ~CGameModule();

  virtual void HandleEvent(Dasher::CEvent *pEvent);
};

游戏模块.cpp

#include "cgamemodule.h"

void CGameModule::HandleEvent(Dasher::CEvent *pEvent) {}

继承自....

namespace Dasher {
  class CEvent;
  class CDasherComponent;
};

class Dasher::CDasherComponent {
 public:
  CDasherComponent() {};
  virtual ~CDasherComponent() {};

  virtual void HandleEvent(Dasher::CEvent * pEvent) {};
};
帖子版权声明 1、本帖标题:未定义对 vtable 的引用
    本站网址:http://xjnalaquan.com/
2、本网站的资源部分来源于网络,如有侵权,请联系站长进行删除处理。
3、会员发帖仅代表会员个人观点,并不代表本站赞同其观点和对其真实性负责。
4、本站一律禁止以任何方式发布或转载任何违法的相关信息,访客发现请向站长举报
5、站长邮箱:yeweds@126.com 除非注明,本帖由AlgorithmAce在本站《unit-testing》版块原创发布, 转载请注明出处!
最新回复 (0)
  • 值得一提的是,我能够避免如下错误:

     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++ 进行链接。

  • 我在尝试实现抽象工厂模式时也遇到了这个问题,但忘记链接一些库。因此,如果什么都没用,请检查是否链接了所有必需的库

  • 提供的所有详细步骤 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 当然应该用这个技巧来解决

    1. 查看类定义。找到第一个非纯虚函数(非 \'= 0\')且由您提供其定义的非内联虚函数(非 \'= default\')。

    来自 JaMIT 的 回答。如果有其他人尝试了上述所有方法但都没有任何效果,那么也许你也像我一样,粗心地忘记 <ClassName>:: 在一个或多个成员函数前面加上前缀。

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

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

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

    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 to Foo' collect2 的函数 vtable 中:错误:ld 返回 1 退出状态

    据我所知,发生这种情况是因为:

    1. p9

    2. p10

  • 注释掉 Q_OBJECT 行使得我的简单测试应用程序使用普通的 g++ *.cpp 构建...(需要一些快速而粗糙的东西,但 qmake 充满了悲伤。)

  • DDV 2月前 0 只看Ta
    引用 8

    因此,我将 Qt 与 Windows XP 和 MinGW 编译器一起使用,这个东西让我抓狂。

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

    对象

    删除所有使函数虚拟、显式和任何你猜测的函数都不起作用。最后我开始逐行删除,结果发现我有

    #ifdef something
    

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

    因此删除所有#ifdef 即可解决问题。

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

  • 在我的情况下,我使用的是 Qt,并 QObject foo.cpp (不是 .h 在末尾 #include "foo.moc" 添加 foo.cpp .

  • 这是 GCC 的一个错误功能。也就是说,G++ 编译器本身无法抱怨未定义的虚拟方法,因为它们可以在其他地方定义。但是 - 它不存储有关缺少哪些虚拟成员的信息;它只存储未定义的 vtable 符号,然后链接器会抱怨它。

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

    GCC 中有一个关于此问题的未解决 bug: bug 42540。 不幸的是,它已经有 13 年历史了 :-(

  • 对于使用 CMakeList.txt 的 Qt 用户

    在您的 CMakeLists.txt 中添加此行: set(CMAKE_AUTOMOC ON)

    正如 Chris Morler 所解释的, 如果你忘记 moc 标题,你会看到这个错误

  • 不是要交叉发布,但是。如果您正在处理 继承, 那么第二个 google 搜索结果就是我所错过的,即应该定义所有虚拟方法。

    例如:

    virtual void fooBar() = 0;
    

    ,请参阅答案 C++ 对 vtable 和继承的未定义引用 。刚刚意识到上面已经提到了,但它可能会对某些人有所帮助。

  • @RyanG:尝试将所有虚拟函数定义移到类定义中。确保它们都在那里,然后看看结果是否发生变化。

  • - 是的,CDasherComponent 在 cpp 中有一个析构函数主体。我发布这篇文章时以为它是在 .h 中声明的。- 已记录。- 那是我在删除文档时错误添加的一个额外括号。- 据我所知,是的。我一直在修改一个我没有编写的 automake 文件,但我一直在遵循对具有相同继承模式的其他类有效的模式,因此除非我犯了一个愚蠢的错误(完全可能),否则我认为不是这样。

    • 你确定 CDasherComponent 有析构函数的主体吗?它肯定不在这里 - 问题是它是否在 .cc 文件中。
    • 从风格角度来说, CDasherModule 应该明确定义其析构函数 virtual .
    • 看起来 CGameModule 在末尾 ( 在 之后 } 有一个额外的内容 }; // for the class
    • 是否 CGameModule 和的 CDasherModule 库相链接 CDasherComponent
  • 引用 16

    GNU C++ 编译器必须决定将其放在何处 vtable

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

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

    作为副作用,请注意,只有对于这个特定的虚拟函数,您才不会得到传统的链接器错误,例如 您缺少函数 foo .

  • 哈哈,C++ 给你太多可能让你不小心搬起石头砸自己的脚的方法了。这看起来是我很可能犯的一个错误。

  • 我刚刚遇到了导致此错误的另一个原因,您可以检查一下。

    基类定义 纯虚函数 为:

    virtual int foo(int x = 0);
    

    并且子类有

    int foo(int x) override;
    

    问题在于拼写错误,应该 "=0" 将 放在括号外面:

    virtual int foo(int x) = 0;
    

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

  • 这里的各种答案都有很多猜测。下面我将给出一个相当简单的代码来重现此错误并解释它发生的原因。

    重现此错误的代码相当少

    IBase.hpp

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

    派生.hpp

    #pragma once
    
    #include "IBase.hpp"
    
    class Derived : public IBase {
        public:
            Derived(int a);
            void action() override;
    };
    

    派生类.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
    

    现在,您可以通过删除 IBase.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
    

    解释

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

    理解此错误的方法如下:链接器正在寻找 IBase 的构造函数。它将需要它作为 Derived 的构造函数。但是,由于 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 指定为库。

  • 确实,当涉及到虚拟事物时,消息似乎与通常的未定义引用 {function/class/struct} 略有不同。让我很失望。

返回
作者最近主题: