2012-08-25 37 views
5

我想知道是否有任何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我發現是從子類構造函數中刪除'內聯' 。也許有一些關於在同一個堆棧框架中放置新內聯構造和重用實例的問題,它們並不能很好地結合在一起。 (更新:進一步檢查顯示新的放置無關緊要)

還有一些研究告訴我,編譯器可以自由地做任何關於'內聯'建議的事情,所以也許其他編譯器沒有這個問題,因爲他們忽略了'內聯'?

+0

你能告訴我們什麼分配器實際上是?你有沒有可能將分配器按值分配給克隆函數? – Arunmu

+0

來源可以在這裏找到。 http://code.google.com/p/box2d/source/browse/trunk/Box2D/Box2D/Common/似乎分配器是無關緊要的,因爲我可以在Clone函數中使用更典型的'new'相同的結果。 – iforce2d

+2

該死的,用C++僞裝的C代碼:x我想你知道你在泄漏內存嗎? (是的,它是無關緊要的,但我想不出任何會導致這種行爲的代碼)。對於相關的評論:程序集爲該方法生成了什麼? –

回答

1

我想你的代碼,並與G ++ 4.5.2版本得到了error: no matching function for call to ‘operator new(long unsigned int, void*&)‘ ...我不知道,但您使用新的語法必須是一個內部的事情...(new(mem)b2CircleShape)

但是,正如Matthieu指出的那樣,這可能不是您想要在C++中執行的操作。創建克隆假設您可以複製對象(和你在你的代碼做一個副本)很簡單:

clone = new b2CircleShape(original); 
+3

'new(mem)b2CircleShape'不是「內部事物」,它是[placement'new'](http://stackoverflow.com/questions/222557/what-uses-are-there-for-placement-new) 。 – DCoder

0

這是顯而易見的。

A)如果它按預期工作一次就好了。 B)錯誤再次發生的事實只能說明它是gcc中的一個錯誤。是的,這些事情確實存在錯誤。除了MSVC編譯器之外,我在所有編譯器中都看到了錯誤。

如果你得到GCC或Clang或其他任何代碼的代碼,並發現錯誤發生的地方,將出現這個錯誤將是由於它讀取的一些標誌。如果它工作一次然後再次失敗,那麼編譯器數據中的標誌或位已經改變,這意味着編譯器中的內存溢出或其他錯字。

對不起。

+1

有趣 - 我只在微軟的'cl'中看到錯誤;) –

+1

也就是說,我沒有像GCC,BCC和Delphi那樣使用它,但是到處都有錯誤,有時編譯器錯誤很簡單! – 2013-02-12 00:00:56