所以,我得到了臭名昭着的可怕
对'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
答案 0 :(得分:353)
GCC FAQ上有一个条目:
解决方案是确保定义所有非纯的虚拟方法。请注意,即使将析构函数声明为pure-virtual [class.dtor] / 7,也必须定义析构函数。
答案 1 :(得分:142)
对于它的价值,忘记虚拟析构函数上的正文会产生以下结果:
未定义对'vtable for CYourClass'的引用。
我正在添加注释,因为错误消息具有欺骗性。 (这是使用gcc版本4.6.3。)
答案 2 :(得分:50)
所以,我已经找到了问题,这是一个错误的逻辑组合,并没有完全熟悉automake / autotools世界。我正在将正确的文件添加到我的Makefile.am模板中,但我不确定构建过程中的哪一步实际创建了makefile本身。所以,我正在使用一个不知道我的新文件的旧makefile进行编译。
感谢您的回复和GCC常见问题解答的链接。我一定会读到这一点,以避免出现这个问题。
答案 3 :(得分:41)
如果您使用的是Qt,请尝试重新运行qmake。如果此错误出现在窗口小部件的类中,则qmake可能未注意到应重新生成ui类vtable。这为我解决了这个问题。
答案 4 :(得分:39)
我只是得到了这个错误,因为我的cpp文件不在makefile中。
答案 5 :(得分:38)
由于以下情况,可能会发生对vtable的未定义引用。试试这个:
A类包含:
virtual void functionA(parameters)=0;
virtual void functionB(parameters);
B类包含:
C类包含:现在您正在编写一个C类,您将在其中从A类派生它。
现在,如果您尝试编译,您将获得对C类的vtable的未定义引用为错误。
原因:
functionA
被定义为纯虚拟,其定义在B类中提供。
functionB
被定义为虚拟(NOT PURE VIRTUAL),因此它尝试在A类中找到它的定义,但是你在B类中提供了它的定义。
解决方案:
virtual void functionB(parameters) =0;
(这是经过测试的)答案 6 :(得分:16)
我刚遇到此错误的另一个原因,您可以检查。
基类将纯虚函数定义为:
#ifndef SERVER_H
#define SERVER_H
#include <QTcpServer>
#include <iostream>
//#include <QtTest/QTest>
#include <QSignalSpy>
#include <QTcpSocket>
#include <QDebug>
//#include <QMessageBox>
#include <QNetworkInterface>
#include <typeinfo>
#include <QStringList>
//#include <QSignalSpy>
QT_BEGIN_NAMESPACE
class QTcpServer;
class QNetworkSession;
QT_END_NAMESPACE
class Server : public QObject
{
Q_OBJECT
public slots:
void sessionOpened();
void sendFortune(void);
void sendData(QPair<QString, QPair<QString, QVariant> > data);
void sendFile(QVariant fileName);
void disconnectServer(void);
void openNewConnection(void);
signals:
void gotNewConnection(QVariant);
private:
QString purpose;
QTcpServer *tcpServer;
QString ipAddr;
quint32 port;
QTcpSocket *clientConnection;
quint32 BlockSize;
bool firstTime;
QSignalSpy * m_pSignalSpy;
QStringList fortunes;
QNetworkSession *networkSession;
//QNetworkConfiguration config;
public:
Server(QString ipAddr, quint32 port, QString purpose = "");
};
#endif // SERVER_H
子类有
virtual int foo(int x = 0);
问题是int foo(int x) override;
应该在括号之外的拼写错误:
"=0"
所以,如果你滚动这么远,你可能找不到答案 - 这是要检查的其他内容。
答案 7 :(得分:15)
这里的各种答案都有很多猜测。我在下面给出了一个相当少的代码来重现这个错误并解释它为什么会发生。
重现此错误的相当简单的代码
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
现在可以通过删除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
<强>解释强>
请注意,上面的代码不需要任何虚拟析构函数,构造函数或任何其他额外的文件来使编译成功(尽管您应该拥有它们)。
理解此错误的方法如下: Linker正在寻找IBase的构造函数。这将需要它为Derived的构造函数。但是,由于Derived会覆盖IBase中的方法,因此它附加了vtable,它将引用IBase。当链接器说&#34;未定义引用IBase的vtable&#34;它基本上意味着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指定为库。
答案 8 :(得分:11)
如果忘记链接到具有定义的目标文件,这很容易发生。
答案 9 :(得分:11)
GNU C ++编译器必须决定放置vtable
的位置,以防您在多个编译单元中定义对象的虚函数(例如,某些对象的虚函数定义在一个.cpp文件,另一个.cpp文件中的其他文件,等等)。
编译器选择将vtable
放在与定义第一个声明的虚函数的位置相同的位置。
现在如果由于某种原因忘记为对象中声明的第一个虚函数提供定义(或者错误地忘记在链接阶段添加编译对象),则会出现此错误。
作为一个副作用,请注意,只有这个特定的虚拟功能,你才能获得传统的链接器错误,比如你缺少函数foo 。
答案 10 :(得分:7)
不要过马路但是。如果您正在处理继承,那么第二次google点击就是我错过的,即。应该定义所有虚拟方法。
如:
virtual void fooBar() = 0;
有关详细信息,请参阅answare C++ Undefined Reference to vtable and inheritance。刚刚意识到它已经在上面提到了,但是它可能对某人有帮助。
答案 11 :(得分:7)
好的,解决方法是您可能错过了定义。请参阅下面的示例以避免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
{
}
答案 12 :(得分:7)
vtable
?在尝试修复该错误消息之前,先了解该错误消息是有用的。我将从一个较高的层次开始,然后深入研究更多细节。这样,一旦人们对vtable的理解感到满意,他们便可以跳过。 …现在有一堆人向前跳过。 :)对于那些坚持不懈的人:
vtable基本上是polymorphism in C++的最常见实现。使用vtable时,每个多态类在程序中的某个位置都有一个vtable。您可以将其视为类的(隐藏)static
数据成员。多态类的每个对象都与其vtable关联,为其最派生的类。通过检查这种关联,程序可以发挥其多态魔力。 重要警告: vtable是实现细节。尽管大多数(所有)C ++编译器都使用vtables实现多态行为,但C ++标准并没有强制要求使用它。我要介绍的细节是典型或合理的方法。 允许编译器与之不同!
每个多态对象都有一个(隐藏的)指向对象最派生类的vtable的指针(在较复杂的情况下,可能有多个指针)。通过查看指针,程序可以知道对象的“真实”类型是什么(构造期间除外,但让我们跳过这种特殊情况)。例如,如果类型为A
的对象没有指向A
的vtable,则该对象实际上是从A
派生的东西的子对象。
名称“ vtable”来自“ v 虚函数表”。它是一个表,用于存储指向(虚拟)函数的指针。编译器为表的布局选择约定。一种简单的方法是按照虚函数在类定义中声明的顺序进行遍历。调用虚拟函数时,程序将跟随对象的指针指向vtable,转到与所需函数关联的条目,然后使用存储的函数指针来调用正确的函数。进行这项工作有很多技巧,但是我在这里不再赘述。
vtable
?vtable由编译器自动生成(有时称为“发射”)。编译器可以在看到多态类定义的每个翻译单元中发出一个vtable,但这通常是不必要的。另一种选择(used by gcc,也可能由其他人选择)是选择一个放置vtable的转换单元,类似于您选择放置一个类的静态数据成员的单个源文件的方式。如果此选择过程未能选择任何翻译单元,则vtable将成为未定义的引用。因此,该错误的信息尚不十分清楚。
类似地,如果选择过程确实选择了翻译单元,但是该目标文件没有提供给链接器,则vtable成为未定义的引用。不幸的是,与选择过程失败的情况相比,在这种情况下错误消息的清晰度甚至更低。 (感谢回答者提到了这种可能性。否则我可能会忘记了。)
如果我们从将(单个)源文件分配给每个需要一个实现类的类的传统开始,gcc使用的选择过程就很有意义。编译该源文件时发出vtable会很好。我们称之为目标。但是,即使不遵循这一传统,选择过程也需要进行。因此,让我们寻找类的特定成员的实现,而不是寻找整个类的实现。如果遵循传统-并且如果该成员实际上得到实施-那么就可以实现目标。
由gcc(可能还有其他编译器)选择的成员是第一个不是纯虚拟的非内联虚拟函数。如果您是在其他成员函数之前声明构造函数和析构函数的人群中的一部分,则该析构函数很可能被选择。 (您确实记得将析构函数虚拟化了,对吗?)有一些例外;例如:我希望最常见的例外是为析构函数提供内联定义时以及请求默认析构函数时(使用“ = default
”)。
精明的人可能会注意到,允许多态类为其所有虚函数提供内联定义。这不是导致选择过程失败吗?在较旧的编译器中也是如此。我读到最新的编译器已经解决了这种情况,但是我不知道相关的版本号。我可以尝试查找它,但是围绕它编码或等待编译器抱怨会更容易。
总而言之,导致“对vtable的引用未定义”错误的三个主要原因:
这些原因本身不足以独自导致错误。而是,这些是您解决错误所要解决的问题。不要期望故意创建其中一种情况肯定会产生此错误;还有其他要求。请确保解决这些情况将解决此错误。
(好的,问这个问题时3号就足够了。)
欢迎大家跳过! :)
= 0
”),并且要提供其定义(不是“ = default
”)。
示例
做什么的细节可能会有所不同,有时会分为不同的问题(例如What is an undefined reference/unresolved external symbol error and how do I fix it?)。不过,我将提供一个示例,说明在特定情况下可能会使新程序员感到困惑的操作。
第1步提到修改您的类,使其具有某种类型的功能。如果您对该功能的描述不屑一顾,则可能是我打算解决的情况。请记住,这是实现目标的一种方式。这不是唯一的方法,在您的特定情况下很容易会有更好的方法。让我们将您的班级称为A
。您的析构函数是否在您的类定义中声明为
virtual ~A() = default;
或
virtual ~A() {}
?如果是这样,只需两个步骤即可将析构函数更改为我们想要的函数类型。首先,将该行更改为
virtual ~A();
第二,将以下行放入项目中的源文件中(如果有的话,最好是具有类实现的文件):
A::~A() {}
这使您的(虚拟)析构函数成为非内联的,并且不是由编译器生成的。 (可以随意进行修改以更好地匹配您的代码格式样式,例如在函数定义中添加标题注释。)
答案 13 :(得分:6)
CDasherComponent
有一个析构函数的主体吗?它肯定不在这里 - 问题是它是否在.cc文件中。CDasherModule
应明确定义其析构函数virtual
。CGameModule
在}
之后有一个额外的}; // for the class
。CGameModule
是否与定义CDasherModule
和CDasherComponent
的图书馆相关联?答案 14 :(得分:5)
也许缺少虚拟析构函数是影响因素?
virtual ~CDasherModule(){};
答案 15 :(得分:5)
这是我的第一个搜索结果,所以我想我要添加另一个要检查的东西:确保虚拟函数的定义实际上在类上。就我而言,我有这个:
标题文件:
class A {
public:
virtual void foo() = 0;
};
class B : public A {
public:
void foo() override;
};
并在我的.cc文件中:
void foo() {
...
}
这应该是
void B::foo() {
}
答案 16 :(得分:3)
也许不是。绝对不见~CDasherModule() {}
。
答案 17 :(得分:3)
所以我在Windows XP和MinGW编译器中使用Qt,这件事让我发疯。
基本上,即使我添加了
,moc_xxx.cpp也是空的Q_OBJECT
删除所有功能虚拟,显式和任何你猜的功能都不起作用。最后我开始逐行删除,结果发现我有
#ifdef something
围绕文件。即使#ifdef为true,也没有生成moc文件。
因此删除所有#ifdef解决了问题。
Windows和VS 2013没有发生这种情况。
答案 18 :(得分: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 ++大师所以我无法解释为什么这更正确但它解决了我的问题。
答案 19 :(得分:2)
提到这个错误有很多可能性,我确定其中很多都会导致错误。就我而言,由于源文件的重复,有同一类的另一个定义。此文件已编译,但未链接,因此链接器抱怨无法找到它。
总结一下,我会说,如果您已经足够长时间地盯着课程并且无法查看可能导致语法问题的语法问题,请查找构建问题,例如丢失的文件或重复的文件
答案 20 :(得分:2)
在我的情况下,我正在使用Qt并在QObject
(非foo.cpp
)文件中定义了.h
子类。解决方法是在#include "foo.moc"
的末尾添加foo.cpp
。
答案 21 :(得分:2)
对于使用 CMakeList.txt 的 Qt 用户
在您的 CMakeLists.txt 中添加这一行:set(CMAKE_AUTOMOC ON)
As explain by Chris Morler 如果您忘记对标头进行模拟,则会出现此错误
答案 22 :(得分:2)
如果所有其他方法都失败了,请查找重复项。我被构造函数和析构函数的显式初始引用误导,直到我在另一篇文章中读取引用。它是任何未解决的方法。在我的情况下,我认为我已经使用char * xml作为参数替换了使用不必要的麻烦的const char * xml的声明,但相反,我创建了一个新的并且保留了另一个。
答案 23 :(得分:1)
我认为还值得一提的是,当您尝试链接到具有至少一个虚拟方法和链接器<的任何类的对象时,也会收到消息。 strong>找不到文件。 例如:
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
vtable中用于Foo'collect2:错误:ld返回1退出 状态
这是由于我不为人知的原因所致:
在编译时为每个类创建Vtable
链接器无权访问Foo.o中的vtable。
答案 24 :(得分:1)
当我遇到一个阻止将对象添加到存档中的make bug时,我遇到了这种类型的错误。
假设我有libXYZ.a,它应该在int中有bioseq.o,但它没有。
我收到了一个错误:
combineseq.cpp:(.text+0xabc): undefined reference to `vtable for bioseq'
这与以上所有不同。我会在存档问题中将此缺少的对象称为。
答案 25 :(得分:1)
我在以下情况中遇到此错误
考虑一种情况,您已在头文件本身中定义了类的成员函数的实现。此头文件是导出的头文件(换句话说,它可能会被复制到某个公共/包含在您的代码库中)。现在您已决定将成员函数的实现分离为.cpp文件。将实现分离/移动到.cpp之后,头文件现在只包含类中成员函数的原型。完成上述更改后,如果您构建代码库,则可能会出现“未定义的'vtable ...'引用错误。”
要解决此问题,请在构建之前确保删除common / include目录中的头文件(您进行了更改)。还要确保更改makefile以容纳/添加从刚刚创建的新.cpp文件构建的新.o文件。当您执行这些步骤时,编译器/链接器将不再抱怨。
答案 26 :(得分: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类逐字切换/粘贴到那里“刚刚工作”。
答案 27 :(得分: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
。
答案 28 :(得分:0)
我尝试了 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
中所做的当然应该通过这个提示来解决
来自 JaMIT's 的回答。
如果有其他人尝试了以上所有方法但没有任何效果,也许您也和我一样,不小心忘记在一个或多个成员函数前添加 <ClassName>::
前缀。
要么我需要检查我的眼睛,要么我需要睡觉。
答案 29 :(得分:0)
我在尝试实现抽象工厂模式时也遇到了这个问题,但忘记链接一些库。因此,如果没有任何帮助,请检查是否链接了所有必需的库
答案 30 :(得分:-2)
我收到此错误只是因为构造函数参数的名称在头文件和实现文件中有所不同。构造函数签名是
PointSet (const PointSet & pset, Parent * parent = 0);
我在实施中写的内容始于
PointSet (const PointSet & pest, Parent * parent)
因此我偶然用“害虫”取代了“pset”。编译器抱怨这一个和另外两个构造函数,其中根本没有错误。我在Ubuntu下使用g ++版本4.9.1。并且在此派生类中定义虚拟析构函数没有任何区别(它在基类中定义)。如果我没有将构造函数的主体粘贴到头文件中,那么我就永远不会发现这个错误,因此在类中定义它们。