2012-08-25 10 views
5

Me pregunto si algún gurú de C++ podría arrojar algo de luz sobre esta extraña situación. Uno de los ejemplos que viene con el motor de física Box2D está fallando con el mensaje "método virtual puro llamado", pero solo con un compilador determinado (y solo en la versión de lanzamiento).¿El constructor en línea repetido dentro del marco de la pila causa el "método virtual puro llamado"?

Box2D como usted sabrá es una porción bastante sólida de código, así que estoy pensando que esto puede ser un problema con el compilador, especialmente dado que solo ocurre con este compilador en particular. Estoy usando mingw32 en Windows 7:

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

A continuación se muestra un extracto reducida de las partes pertinentes de Box2D. Se puede extraer de la fuente completa en:

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

Nota la ubicación nueva en la función Clonar.

Ahora la ejecución que causa el problema se reduce a esto:

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

Después de educar a mí mismo en la forma de un método virtual cada vez que se podría llamar, en primer lugar, he tratado de averiguar por qué estaba ocurriendo aquí , ya que no se ajusta al caso clásico de llamar a una función virtual en el constructor de la clase base. Después de una sesión prolongada de tropezar a ciegas, presenté el caso mínimo anterior.

Creo que el compilador es lo suficientemente inteligente como para ver que estas dos instancias de b2CircleShape no están en uso en el mismo ámbito, por lo que solo asigna espacio para uno y lo reutiliza. Después de la primera instancia se destruye, el vtable es como se esperaba, con la manguera. Entonces, por alguna razón, cuando se construye la segunda instancia, el vtable no se construye de nuevo ...

Se me ocurrieron dos cosas que evitan el problema, pero como digo, parece más un problema de compilación, así que no estoy sugiriendo que se deba cambiar este código.

El número de revisión dudoso 1 es para comentar la definición del destructor virtual en la clase base. Toda la información que he leído sobre este tema sugiere que esta no es la respuesta. (Curiosamente, encontré que no era suficiente simplemente eliminar el modificador 'virtual' del destructor de la clase base. Tengo entendido que el compilador proporcionaría un destructor predeterminado ~ b2Shape() {} si no se especificaba ninguno, entonces ¿por qué el resultado es diferente? si realmente especifico cuál sería el valor predeterminado de todos modos? Bueno, esto está al lado del punto realmente ...)

Número de arreglo no tan dudoso 2 que descubrí fue para eliminar el 'en línea' del constructor de subclase . Tal vez haya algo sobre la colocación nueva, la construcción en línea y las instancias reutilizadas en el mismo marco de pila que no se combinan muy bien. (ACTUALIZACIÓN: más comprobaciones muestran que la ubicación nueva es irrelevante)

Algunas investigaciones adicionales me dicen que el compilador puede hacer lo que quiera con sugerencias 'en línea', por lo que quizás los otros compiladores no tengan este problema porque están ignorando el 'en línea'?

+0

¿Puede mostrarnos qué asignador realmente es? ¿Estás por casualidad pasando el asignador por valor a la función de clonación? – Arunmu

+0

La fuente se puede encontrar aquí. http://code.google.com/p/box2d/source/browse/trunk/Box2D/Box2D/Common/ Parece que el asignador es irrelevante, porque puedo usar el más típico 'nuevo' en la función Clonar con el mismos resultados. – iforce2d

+2

Maldición, código C en un disfraz de C++: x Supongo que sabe que está perdiendo memoria. (Sí, es irrelevante, pero no puedo pensar en nada en el código presentado que produzca dicho comportamiento). Y para un comentario relevante: ¿cuál es el conjunto generado para el método considerado? –

Respuesta

1

Probé tu código y obtuve error: no matching function for call to ‘operator new(long unsigned int, void*&)‘ con g ++ versión 4.5.2 ... No estoy seguro, pero la nueva sintaxis que usas debe ser una cosa interna ...(new (mem) b2CircleShape)

Sin embargo, como señaló Matthieu, eso probablemente no es lo que quieres hacer en C++. Creación de un clon suponiendo que usted puede copiar sus objetos (y lo hace una copia del código) es simplemente:

clone = new b2CircleShape(original); 
+3

'new (mem) b2CircleShape' no es" una cosa interna ", es [placement' new'] (http://stackoverflow.com/questions/222557/what-uses-are- there-for-placement -new) . – DCoder

0

Esto es obvio.

A) Si funciona una vez como se esperaba, entonces bien. B) El hecho de que el error vuelva a ocurrir solo puede significar que es un error en su gcc. Sí, es cierto que estas cosas pueden tener errores. He visto errores en todos los compiladores a excepción del compilador de MSVC.

Ese error allí si obtiene el código para GCC o Clang o lo que sea y encuentra dónde ocurre el error será debido a algunos indicadores que se lee. Si funciona una vez y luego vuelve a fallar, las banderas o los bits han cambiado en los datos de los compiladores y eso significa un rebasamiento de la memoria u otro error tipográfico en el compilador.

Lo siento.

+1

Divertido - Solo he visto errores en 'cl' de Microsoft;) –

+1

Dicho esto, no lo usé tanto como GCC, BCC y Delphi, pero hay errores en todas partes y ¡a veces los errores del compilador son así de simples! –

Cuestiones relacionadas