【问题标题】:C++ virtual functions.Problem with vtable [duplicate]C ++虚函数。vtable问题[重复]
【发布时间】:2011-06-25 23:55:07
【问题描述】:

可能重复:
GCC C++ Linker errors: Undefined reference to 'vtable for XXX', Undefined reference to 'ClassName::ClassName()'

我正在用 C++ 做一个小项目,但遇到了一些关于虚函数的问题。

我有一个带有一些虚函数的基类:

#ifndef COLLISIONSHAPE_H_
#define COLLISIONSHAPE_H_


namespace domino
{
class CollisionShape : public DominoItem
{
public:
// CONSTRUCTOR
//-------------------------------------------------

// SETTERS
//-------------------------------------------------

// GETTERS
//-------------------------------------------------

    virtual void GetRadius() = 0;
    virtual void GetPosition() = 0;
    virtual void GetGrowth(CollisionShape* other) = 0;
    virtual void GetSceneNode();

// OTHER
//-------------------------------------------------

    virtual bool overlaps(CollisionShape* shape) = 0;

};
}

#endif /* COLLISIONSHAPE_H_ */

还有一个 SphereShape 类,它扩展了 CollisionShape 并实现了上述方法

/* SphereShape.h */

#ifndef SPHERESHAPE_H_
#define SPHERESHAPE_H_

#include "CollisionShape.h"

namespace domino
{
class SphereShape : public CollisionShape
{

public:
// CONSTRUCTOR
//-------------------------------------------------
    SphereShape();
    SphereShape(CollisionShape* shape1, CollisionShape* shape2);

// DESTRUCTOR
//-------------------------------------------------

    ~SphereShape();

// SETTERS
//-------------------------------------------------
    void SetPosition();
    void SetRadius();

// GETTERS
//-------------------------------------------------

    void GetRadius();
    void GetPosition();
    void GetSceneNode();
    void GetGrowth(CollisionShape* other);

// OTHER
//-------------------------------------------------

    bool overlaps(CollisionShape* shape);
};
}

#endif /* SPHERESHAPE_H_ */

和 .cpp 文件:

/*SphereShape.cpp*/
 #include "SphereShape.h"

#define max(a,b) (a>b?a:b)

namespace domino
{

// CONSTRUCTOR
//-------------------------------------------------

SphereShape::SphereShape(CollisionShape* shape1, CollisionShape* shape2)
{
}

// DESTRUCTOR
//-------------------------------------------------

SphereShape::~SphereShape()
{
}

// SETTERS
//-------------------------------------------------

void SphereShape::SetPosition()
{
}

void SphereShape::SetRadius()   
{
}

// GETTERS
//-------------------------------------------------

void SphereShape::GetRadius()   
{

}

void SphereShape::GetPosition()   
{  
}


void SphereShape::GetSceneNode()
{
}

void SphereShape::GetGrowth(CollisionShape* other)
{
}

// OTHER
//-------------------------------------------------

bool SphereShape::overlaps(CollisionShape* shape)
{
     return true;
}

}

这些类以及其他一些类被编译到一个共享库中。

Building libdomino.so
g++ -m32 -lpthread -ldl -L/usr/X11R6/lib -lglut -lGLU -lGL -shared     -lSDKUtil  -lglut  -lGLEW  -lOpenCL   -L/home/adrian/AMD-APP-SDK-v2.4-lnx32/lib/x86  -L/home/adrian/AMD-APP-SDK-v2.4-lnx32/TempSDKUtil/lib/x86 -L"/home/adrian/AMD-APP-SDK-v2.4-lnx32/lib/x86"   -lSDKUtil  -lglut  -lGLEW  -lOpenCL -o build/debug/x86/libdomino.so build/debug/x86//Material.o build/debug/x86//Body.o build/debug/x86//SphereShape.o build/debug/x86//World.o build/debug/x86//Engine.o build/debug/x86//BVHNode.o

当我编译使用此库的代码时,我收到以下错误:

../../../lib/x86//libdomino.so: undefined reference to `vtable for domino::CollisionShape'
../../../lib/x86//libdomino.so: undefined reference to `typeinfo for domino::CollisionShape'

用于编译使用该库的演示的命令:

g++ -o build/debug/x86/startdemo build/debug/x86//CMesh.o build/debug/x86//CSceneNode.o build/debug/x86//OFF.o build/debug/x86//Light.o build/debug/x86//main.o build/debug/x86//Camera.o -m32 -lpthread -ldl -L/usr/X11R6/lib -lglut -lGLU -lGL  -lSDKUtil  -lglut  -lGLEW  -ldomino  -lSDKUtil  -lOpenCL   -L/home/adrian/AMD-APP-SDK-v2.4-lnx32/lib/x86  -L/home/adrian/AMD-APP-SDK-v2.4-lnx32/TempSDKUtil/lib/x86  -L../../../lib/x86/ -L"/home/adrian/AMD-APP-SDK-v2.4-lnx32/lib/x86" 

-ldomino 标志)

当我运行演示时,我手动告诉它有关库的信息:

LD_LIBRARY_PATH=../../lib/x86/:$AMDAPPSDKROOT/lib/x86:$LD_LIBRARY_PATH bin/x86/startdemo 

在阅读了一些关于虚函数和虚表的内容后,我了解到虚表是由编译器处理的,我不应该担心它,所以我对如何处理这个问题有点困惑。

我正在使用gcc version 4.6.0 20110530 (Red Hat 4.6.0-9) (GCC)

后期编辑: 真的很抱歉,我这里是直接手写代码的。 我已经在代码中定义了返回类型。 我向下面回答的 2 人道歉。

我不得不提一下,我是在 C++ 中使用更复杂项目布局的初学者。我的意思是更复杂的 makefile、共享库之类的东西。


我的问题是因为我没有定义 virtual void CollisionShape::GetSceneNode() 的主体。

解决这个问题的方法是定义上述函数,或者将其声明为纯虚函数:

virtual void CollisionShape::GetSceneNode() = 0;

【问题讨论】:

  • @adivasile:请发布一个最小的 sn-p 编译并显示问题。当你去解决没有返回类型的问题时,你颠倒了函数返回的类型。当存在与实际情况无关的红鲱鱼时,有时很难诊断错误。还请提供使用该库的代码的命令行,因为这是您看到错误的地方。
  • @Michael Burr:我添加了用于链接库的代码。至于 sn-p,我不确定我是否可以提供,因为涉及很多文件和依赖项。
  • 正如发布的那样,您的代码正在自找麻烦。 CollisionShape 需要一个虚拟析构函数。也许您声明了它,但忘记定义了?很难说有一个不可编译的 sn-p。
  • 接近可编译了。将返回类型替换为void,删除不相关的包含和函数体,就是这样。
  • 好的,我添加了两个类的完整定义,并删除了返回类型和包含。如果我遗漏了什么,请告诉我。

标签: c++ virtual-functions vtable typeinfo


【解决方案1】:

任何非纯虚函数都必须定义,即使它从未使用过。缺少定义通常会导致“vtable undefined”链接器错误,尤其是当类的第一个非纯非内联虚函数未定义时。在您的情况下,CollisionShape::GetSceneNode() 未定义。

另外,每个具有虚函数的类都需要一个虚析构函数,每次都绝对没有例外。不幸的是,语言并没有强制执行这一点,所以这是你的责任。 G++ 有一个标志,-Weffc++,它可以对这个和其他常见的陷阱发出警告,在 Scott Meyers 的书“Effective C++”中有描述。我强烈建议一直使用这个标志。默认使用 -Werror 也是一个好习惯。仅在无法修复代码的情况下,逐个禁止单个警告。

【讨论】:

  • -1 “每个具有虚函数的类都需要一个虚析构函数”。作为一个反例,COMIUnknown 接口具有三个纯虚函数,并且它没有虚析构函数。如果您有一台 Windows 机器,那么您使用的软件相当多,而这些软件又使用从该类派生的数千个类,您可以将其乘以 Windows 机器的数量:显然它工作得很好。
  • COM 接口依赖于Release 机制,该机制否定了没有虚拟析构函数的影响。也就是说,由于您永远不会在 IUnknown 上调用 delete,因此不需要虚拟析构函数行为。在大多数情况下,您会对支持在某些接口指针上使用 delete 感兴趣,因此通常需要虚拟析构函数。
【解决方案2】:

我不能确定这是否会解决您的编译错误,但无论如何您都需要在您的CollisionShape 类中声明一个虚拟析构函数(virtual ~CollisionShape())。当SphereShape 通过其基类指针(指向CollisionShape 的指针)删除时,如果不这样做,将导致未定义的runtime 行为。当然,由于确实在类的vtbl 中添加了一个虚拟构造函数,所以我想这不是错误的罪魁祸首。

在 Effective C++ 中,Scott Meyers 有以下说法。

C++ 语言标准在这个主题上异常清晰。当您尝试通过基类指针删除派生类对象并且基类具有 nonvirtual 析构函数 [...] 时,结果未定义。这意味着编译器可以生成代码来做任何他们喜欢的事情:重新格式化你的磁盘,向你的老板发送暗示性邮件,将源代码传真给你的竞争对手,等等。 (在运行时经常发生的是派生类的析构函数永远不会被调用。[...])

【讨论】:

  • 感谢您的回答,但我已经在 n.m. 的帮助下确定了问题。我没有实现 CollisionShape::GetSceneNode(),也没有将其声明为纯虚拟。
  • @adivasile 很高兴听到您解决了您的问题!为了防止更多人尝试回答您的问题(并帮助可能有相同问题的其他人),请在此处发布您的答案并接受它。不过,正如我所说,您仍然需要一个虚拟析构函数即使您的代码可以编译
【解决方案3】:

由于GetSceneNode 的声明冲突,在您当前的代码中

virtual void GetSceneNode();

在基类中,并且

SceneNode* GetSceneNode();

在派生类中,您的代码不应编译。您不应该进入链接阶段。我很确定您提供的代码不是您的真实代码。

因此,我对这个问题投了反对票。

但是关于您显然使用其他代码产生的错误,之前已经在 SO 上询问过,并回答了例如here

因此,我也投票结束了这个问题。

干杯,

【讨论】:

  • 源代码中的定义是正确的。两者都有 SceneNode* 作为返回类型。当我删除返回类型以使 sn-p 可编译时,我忽略了该声明并道歉。因为我已经指定库编译,你应该意识到这不是问题。
  • 您建议人们使用哪些标准来了解您所说的哪些部分是正确的,哪些部分是不正确的?
【解决方案4】:

这可能不是全部问题,但您缺少这些函数的返回类型。即

virtual double GetPosition() = 0;

【讨论】:

  • 不,返回类型是cl_float,在别处定义。
  • 你能发布继承的类以及如何编译它们
猜你喜欢
  • 2017-05-31
  • 2011-07-22
  • 1970-01-01
  • 2013-07-30
  • 2010-11-25
  • 1970-01-01
  • 2011-05-25
  • 1970-01-01
  • 2011-11-29
相关资源
最近更新 更多