堆栈框架内重复的内联构造函数导致“纯虚方法调用”?

时间:2012-08-25 14:10:43

标签: c++ inheritance box2d vtable mingw32

我想知道是否有任何C ++专家可以对这种奇怪的情况有所了解。 Box2D物理引擎附带的一个例子是消息“称为纯虚方法”,但仅限于某个编译器(仅在发布版本中)。

你可能知道Box2D是一个非常可靠的代码块,所以我认为这可能是编译器的一个问题,特别是考虑到它只发生在这个特定的编译器上。我在Windows7上使用mingw32:

> gcc.exe --version
gcc version 4.4.0 (GCC)

以下是Box2D相关部分的简要摘录。您可以在以下网址查看完整资料来源:

b2Shape.h
b2CircleShape.h
b2CircleShape.cpp
SensorTest.h

//base class
class b2Shape
{
public:
    virtual ~b2Shape() {}
    virtual b2Shape* Clone(b2BlockAllocator* allocator) const = 0;
};


//sub class
class b2CircleShape : public b2Shape
{
public:
    b2CircleShape();
    b2Shape* Clone(b2BlockAllocator* allocator) const;
};

inline b2CircleShape::b2CircleShape() {}

b2Shape* b2CircleShape::Clone(b2BlockAllocator* allocator) const
{
    void* mem = allocator->Allocate(sizeof(b2CircleShape));
    b2CircleShape* clone = new (mem) b2CircleShape;
    *clone = *this;
    return clone;
}

请注意克隆功能中的新位置。

现在导致问题的执行归结为:

{
    b2CircleShape shape;
    shape.Clone(allocator); //ok
}
{
    b2CircleShape shape;
    shape.Clone(allocator); //"pure virtual method called"
}

在教育自己如何首先调用虚拟方法之后,我试图弄清楚它为什么会在这里发生,因为它不适合在基类构造函数中调用虚函数的经典情况。经过长时间盲目磕磕绊绊之后,我想出了上面的最小案例。

我的猜测是,编译器足够智能,可以看到这两个b2CircleShape实例未在同一范围内使用,因此它只为一个实例分配空间并重用它。在第一个实例被破坏后,vtable如预期的那样被冲洗。然后由于某种原因,当构造第二个实例时,vtable不再构造......?

我想出了两个可以避免这个问题的东西,但就像我说它看起来更像是一个编译器问题所以我并不是说这个代码需要改变。

可疑的修复号1是注释掉基类中的虚拟析构函数定义。我在这个问题上阅读的所有信息都表明这不是答案。 (有趣的是,我发现仅仅从基类析构函数中删除'virtual'修饰符是不够的。我的理解是编译器会提供默认的析构函数~b2Shape(){}如果没有指定,那么为什么结果不同如果我实际上指定了默认值是什么?嗯,这真的不是真正的......)

我发现的不太可疑的修复2是从子类构造函数中删除'inline'。也许有一些关于放置新的内联构造和重复使用的实例在同一堆栈框架中不能很好地一起使用。 (更新:进一步检查显示新的展示位置无关紧要)

更多的研究告诉我,编译器可以自由地做任何关于“内联”建议的事情,所以也许其他编译器没有这个问题,因为他们忽略了'内联'?

2 个答案:

答案 0 :(得分:1)

我尝试了你的代码并使用g ++ 4.5.2版获得了error: no matching function for call to ‘operator new(long unsigned int, void*&)‘ ...我不确定你使用的新语法必须是内部的东西...(new(mem)b2CircleShape)< / p>

然而,正如Matthieu指出的那样,这可能不是你想用C ++做的。假设您可以复制对象(并在代码中复制),创建克隆只是:

clone = new b2CircleShape(original);

答案 1 :(得分:1)

这很明显。

A)如果它按预期工作一次那么罚款。 B)错误再次发生的事实只能意味着它是你的gcc中的一个错误。是的,这些事情确实存在错误。除了MSVC编译器之外,我在所有编译器中都看到了错误。

如果您获得GCC或Clang或其他任何代码并找到错误发生的位置,那里的错误将归因于它读取的一些标志。如果它工作一次然后再次失败,那么编译器数据中的标志或位已经改变,这意味着编译器中的内存溢出或其他错字。

对不起。