我想知道是否有任何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是在基類註釋掉虛擬析構函數定義。我在這個問題上讀到的所有信息都表明這不是答案。 (有趣的是,我發現這是不夠的,只是刪除從基類的析構函數「虛擬」修改器。我的理解是,編譯器會提供一個默認的析構函數〜b2Shape(){}如果沒有指定過,那麼爲什麼是不同的結局如果我真的指定什麼默認是反正?那麼,這是真的旁邊點...)
不太可疑的修復號碼2我發現是從子類構造函數中刪除'內聯' 。也許有一些關於在同一個堆棧框架中放置新內聯構造和重用實例的問題,它們並不能很好地結合在一起。 (更新:進一步檢查顯示新的放置無關緊要)
還有一些研究告訴我,編譯器可以自由地做任何關於'內聯'建議的事情,所以也許其他編譯器沒有這個問題,因爲他們忽略了'內聯'?
你能告訴我們什麼分配器實際上是?你有沒有可能將分配器按值分配給克隆函數? – Arunmu
來源可以在這裏找到。 http://code.google.com/p/box2d/source/browse/trunk/Box2D/Box2D/Common/似乎分配器是無關緊要的,因爲我可以在Clone函數中使用更典型的'new'相同的結果。 – iforce2d
該死的,用C++僞裝的C代碼:x我想你知道你在泄漏內存嗎? (是的,它是無關緊要的,但我想不出任何會導致這種行爲的代碼)。對於相關的評論:程序集爲該方法生成了什麼? –