C++虚拟函数.vtable的问题

C++ virtual functions.Problem with vtable

本文关键字:问题 vtable 函数 虚拟 C++      更新时间:2023-10-16

可能重复:
GCC C++链接器错误:对';vtable for XXX';,未定义对';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_ */

以及扩展CCD_ 2并实现上述方法的CCD_

/* 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)

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

我必须提到,我是C++中使用更复杂项目布局的初学者。我指的是更复杂的makefile、共享库之类的东西。


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

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

virtual void CollisionShape::GetSceneNode() = 0;

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

与此无关的是,每个具有虚拟函数的类都需要一个虚拟析构函数,每次都绝对没有例外。不幸的是,语言并没有强制执行这一点,所以这是你的责任。G++有一个标志-Weffc++,它可以对这个和其他常见的陷阱发出警告,正如Scott Meyers的书《有效的C++》中所描述的那样。我强烈建议您始终使用此标志。默认情况下使用-Werror也是一个好习惯。逐个取消单个警告,并且仅在无法修复代码的情况下。

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

在Effective C++中,Scott Meyers有以下几点要说。

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

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

virtual void GetSceneNode();

在基类中,以及

SceneNode* GetSceneNode();

在派生类中,代码不应编译。你不应该进入链接阶段。我非常确信您所呈现的代码不是您真正的代码。

因此,我否决了这个问题。

但是,关于您显然在其他代码中产生的错误,以前在SO上有人问过它,例如在这里得到了回答。

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

干杯&hth。,

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

virtual double GetPosition() = 0;