C++虚函数。vtable 问题

发布于 2024-11-17 02:16:42 字数 4805 浏览 6 评论 0原文

可能的重复:
GCC C++ 链接器错误:未定义的引用“XXX 的 vtable”,对“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_ */

以及一个扩展了 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 类:

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

Possible Duplicate:
GCC C++ Linker errors: Undefined reference to 'vtable for XXX', Undefined reference to 'ClassName::ClassName()'

I'm doing a little project in C++ and I've come into some problems regarding virtual functions.

I have a base class with some virtual functions:

#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_ */

and a SphereShape class which extends CollisionShape and implements the methods above

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

and the .cpp file:

/*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;
}

}

These classes, along some other get compiled into a shared library.

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

When I compile the code that uses this library I get the following error:

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

Command used to compile the demo that uses the library:

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" 

(the -ldomino flag)

And when I run the demo, I manually tell it about the library:

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

After reading a bit about virtual functions and virtual tables I understood that virtual tables are handled by the compiler and I shouldn't worry about it, so I'm a little bit confused on how to handle this issue.

I'm using gcc version 4.6.0 20110530 (Red Hat 4.6.0-9) (GCC)

Later edit:
I'm really sorry, but I wrote the code by hand directly here.
I have defined the return types in the code.
I apologize to the 2 people that answered below.

I have to mention that I am a beginner at using more complex project layouts in C++.By this I mean more complex makefiles, shared libraries, stuff like that.


My problem was caused by the fact that I didn't define the body of virtual void CollisionShape::GetSceneNode().

The way to fix this is to either define the above function, or declare it as pure virtual as such:

virtual void CollisionShape::GetSceneNode() = 0;

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

属性 2024-11-24 02:16:42

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

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

Any non-pure virtual function must be defined, even if it's never used. A missing definition will often result in the 'vtable undefined' linker error, especially when the very first non-pure, non-inline virtual function of a class is left undefined. In your case, CollisionShape::GetSceneNode() is left undefined.

On an unrelated note, every class that has a virtual function needs a virtual destructor, every time, absolutely no exceptions whatsoever. The language unfortunately doesn't enforce this, so it's your responsibility. G++ has a flag, -Weffc++, which enables warnings for this and other common pitfalls, described in the Scott Meyers's book "Effective C++". I strongly recommend using this flag all the time. Using -Werror by default is also a good habit. Suppress individual warnings one by one, and only if there's no possibility to fix the code.

也只是曾经 2024-11-24 02:16:42

我不能肯定地说这是否会修复您的编译错误,但无论如何您都需要在您的 <代码>CollisionShape 类。如果不这样做,当通过其基类指针(指向 CollisionShape 的指针)删除 SphereShape 时,将导致未定义的运行时行为。当然,由于虚拟构造函数确实被添加到类的 vtbl 中,所以我想这也有可能是错误背后的罪魁祸首。

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

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

I can't say for certain whether this will fix your compilation error, but in any case you need to declare a virtual destructor (virtual ~CollisionShape()) in your CollisionShape class. Failure to do so will result in undefined runtime behavior when SphereShape is deleted through its base class pointer (a pointer to CollisionShape). Of course, since a virtual constructor is indeed added to a class's vtbl, I guess it's not beyond the realm of possibility that this is the culprit behind your error.

In Effective C++, Scott Meyers has the following to say.

The C++ language standard is unusually clear on this topic. When you try to delete a derived class object through a base class pointer and the base class has a nonvirtual destructor [...], the results are undefined. That means compilers may generate code to do whatever they like: reformat your disk, send suggestive mail to your boss, fax source code to your competitors, whatever. (What often happens at runtimme is that the derived class's destructor is never called. [...])

最初的梦 2024-11-24 02:16:42

由于 GetSceneNode 的声明存在冲突,因此

virtual void GetSceneNode();

在基类和

SceneNode* GetSceneNode();

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

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

但是关于您显然用其他代码产生的错误,之前已经有人问过它,并回答了例如 这里

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

干杯&呵呵,

Due to the conflicting declarations of GetSceneNode, in your current code

virtual void GetSceneNode();

in the base class, and

SceneNode* GetSceneNode();

in the derived class, your code should not compile. You should not get to the linking stage. I'm pretty sure that the code that you're presenting is not your real code.

Hence, I downvoted the question.

But regarding the error that you evidently produced with some other code, it has been asked before on SO, and answered for example here.

Hence, I also voted to close the question.

Cheers & hth.,

烟柳画桥 2024-11-24 02:16:42

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

virtual double GetPosition() = 0;

This may not be the whole problem, but you are missing a return type on those functions. ie

virtual double GetPosition() = 0;
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文