当构建我的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>::前缀。
要么我需要检查一下眼睛,要么我需要睡一会儿。
这里有很多猜测,各种各样的答案。下面我将给出一个相当小的代码来重现这个错误,并解释为什么会出现这个错误。
重现此错误的代码相当少
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指定为库。