2012-08-25 11 views
5

Ich frage mich, ob irgendwelche C++ - Gurus da draußen diese seltsame Situation beleuchten könnten. Eines der Beispiele, das mit der Box2D-Physik-Engine geliefert wird, stürzt mit der Meldung "reine virtuelle Methode genannt" ab, aber nur mit einem bestimmten Compiler (und nur im Release-Build).wiederholte Inline-Konstruktor in Stack-Frame verursacht "reine virtuelle Methode namens"?

Box2D wie Sie vielleicht wissen, ist ein ziemlich solides Stück Code, so denke ich, dass dies ein Problem mit dem Compiler sein kann, vor allem, dass es nur mit diesem bestimmten Compiler passiert. Ich verwende mingw32 auf Windows7:

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

Nachfolgend finden Sie eine abgespeckte Auszug der relevanten Teile von Box2D. Sie können die vollständige Quelle Besuche bei:

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

Hinweis die Platzierung neu in der Clone-Funktion.

Nun ist die Ausführung, die das Problem verursacht, dies läuft darauf hinaus:

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

Nachdem ich mich auf, wie eine virtuelle Methode Erziehung immer an erster Stelle genannt werden kann, versuchte ich, warum es hier um herauszufinden, geschieht , da es nicht in den klassischen Fall des Aufrufs einer virtuellen Funktion im Basisklassenkonstruktor passt. Nach einer längeren Sitzung, in der ich blind herumstolperte, kam ich auf den Minimalfall.

Meine wilde Vermutung ist, dass der Compiler schlau genug ist zu sehen, dass diese beiden b2CircleShape-Instanzen nicht im selben Bereich verwendet werden, so dass nur Platz für einen reserviert und wiederverwendet wird. Nachdem die erste Instanz zerstört wurde, wird die vtable wie erwartet, abgespritzt. Aus irgendeinem Grund, wenn die zweite Instanz konstruiert wird, wird die vtable nicht wieder aufgebaut ...?

Ich kam mit zwei Dingen, die das Problem vermeiden, aber wie ich sagen, es scheint eher ein Compiler-Problem, so dass ich nicht vorschlage, dass dieser Code geändert werden muss.

Dubious Fix Nummer 1 ist die virtuelle Destruktordefinition in der Basisklasse auskommentieren. Alle Informationen, die ich zu diesem Thema gelesen habe, deuten darauf hin, dass dies nicht die Antwort ist. (Interessanterweise fand ich es nicht genug, einfach den 'virtuellen' Modifikator aus dem Basisklassen-Destruktor zu entfernen. Ich verstehe, dass der Compiler einen Standard-Destruktor ~ b2Shape() {} bereitstellen würde, wenn keiner angegeben wurde. Warum unterscheidet sich das Ergebnis wenn ich eigentlich angeben würde, was der Standardwert wäre? Nun, das ist neben dem Punkt wirklich ...)

Nicht-so-dubiose Fix Nummer 2 Ich entdeckte, dass 'Inline' aus dem Unterklasse-Konstruktor entfernen . Vielleicht gibt es etwas über die Platzierung neuer, Inline-Konstruktionen und die wiederverwendeten Instanzen im selben Stack-Frame, die nicht gut zusammenspielen. (UPDATE: weitere Überprüfung zeigt, dass die Platzierung neu irrelevant ist)

Einige weitere Untersuchungen sagen mir, dass der Compiler frei ist, was auch immer er in Bezug auf "Inline" Vorschläge tun mag, so dass die anderen Compiler dieses Problem nicht haben Sie ignorieren die "Inline"?

+0

Können Sie uns zeigen, welcher Zuordner tatsächlich ist? Übergeben Sie zufällig Zuteiler wertmäßig an die Klonfunktion? – Arunmu

+0

Die Quelle finden Sie hier. http://code.google.com/p/box2d/source/browse/trunk/Box2D/Box2D/Common/ Es scheint, dass der Zuordner jedoch irrelevant ist, weil ich das typischere "neue" in der Klonfunktion mit dem verwenden kann gleiche Ergebnisse. – iforce2d

+2

Verdammt, C-Code in einer C++ - Verkleidung: x Ich nehme an, Sie wissen, dass Sie Speicher verlieren? (Ja, es ist irrelevant, aber ich kann mir nichts in dem vorgelegten Code vorstellen, der ein solches Verhalten hervorbringen würde). Und für einen relevanten Kommentar: Was ist die Assembly, die für die betrachtete Methode generiert wurde? –

Antwort

1

Ich habe versucht Ihren Code und habe error: no matching function for call to ‘operator new(long unsigned int, void*&)‘ mit g ++ Version 4.5.2 ... Ich bin mir nicht sicher, aber die neue Syntax, die Sie verwenden, muss eine interne Sache sein ...(new (mem) b2CircleShape)

Wie Matthieu jedoch schon sagte, ist das wahrscheinlich nicht das, was Sie in C++ machen wollen. einen Klon Erstellen vorausgesetzt, Sie Ihre Objekte kopieren können (und Sie eine Kopie in Ihrem Code zu tun) ist einfach:

clone = new b2CircleShape(original); 
+3

'neu (mem) b2CircleShape' ist nicht" eine interne Sache ", es ist [Platzierung' neu'] (http://stackoverflow.com/questions/222557/what-uses-are-there-for-placement-new) . – DCoder

0

Das liegt auf der Hand.

A) Wenn es einmal wie erwartet funktioniert, dann gut. B) Die Tatsache, dass der Fehler erneut auftritt, kann nur bedeuten, dass es sich um einen Fehler in Ihrem gcc handelt. Ja, es stimmt, dass diese Dinge Fehler haben können. Ich habe Bugs in allen Compilern mit Ausnahme des MSVC-Compilers gesehen.

Dieser Fehler dort, wenn Sie den Code für GCC oder Clang oder was auch immer und finden Sie, wo der Fehler passiert, wird aufgrund einiger Flags, die es liest. Wenn es einmal funktioniert und dann erneut fehlschlägt, dann haben sich die Flags oder Bits in den Compiler-Daten geändert, und das bedeutet einen Speicherüberlauf oder einen anderen Tippfehler im Compiler.

Entschuldigung.

+1

Lustig - Ich habe nur Fehler in Microsofts 'cl' gesehen;) –

+1

Das heißt, ich habe es nicht so oft verwendet wie GCC, BCC und Delphi, aber es gibt Fehler überall und manchmal sind die Compilerfehler so einfach! –