2010-02-12 12 views
6

tengo una clase simple en C++ que tiene un entero y una vtable:objeto MSVC Disposición Quirk

class Something { 

    virtual void sampleVirtualMethod(); 

    int someInteger; 
}; 

Si nos fijamos en el diseño de objetos para MSVC (utilizando el/d1reportSingleClassLayout) se obtiene:

class Something  size(8): 
     +--- 
0  | {vfptr} 
4  | someInteger 
     +--- 

Lo cual tiene mucho sentido. 4 bytes para el puntero vtable y 4 bytes para el entero. Lo extraño es cuando agrego un doble a la clase:

class Something {  
    virtual void sampleVirtualMethod(); 
    int someInteger; 
    **double someDouble;** 
}; 

recibí este diseño de objetos:

class Something  size(24): 
     +--- 
0  | {vfptr} 
8  | someInteger 
     | <alignment member> (size=4) 
16  | someDouble 
     +--- 

¿Por qué es la diferencia entre el 0 y el desplazamiento someInteger 8 en lugar de 4? ¿El vtable creció a 8 bytes de alguna manera? No importa el orden de cuando agregue un doble, esto sucede.

Gracias.

+0

No puedo evitar preguntarme por qué le interesaría esto. ¿Esto está causando un problema directo a lo que estás trabajando? –

+3

¿Por qué * no * le importa? Es sorprendente para el OP (y para mí). ¿Dónde está el daño al tratar de aprender cosas nuevas? ;) – jalf

+0

Estoy intentando fusionar modelos de objetos entre binarios y binarios de Visual Studio. En realidad es un problema :). Lo extraño es que tanto GCC como Visual Studio diseñan objetos como este mientras que llvm no. – Mason

Respuesta

0

No puedo responder su pregunta directamente porque no hay una buena excusa para el comportamiento del compilador. En lugar de eso, me entrego a la especulación salvaje ya que todavía no ha habido una respuesta.

I sospechas de un error en el algoritmo de alineación que es algo como esto:

  • alineación de una estructura (es decir, la colocación del primer miembro) debe ser al menos tan ancha como la alineación de su miembro más amplia
  • Uy, se olvidó de contar el puntero de la tabla de función virtual como miembro

Si existe este error sospecho que es un remanente de los primeros días del compilador como un compilador de C-alineado de 4 bytes. Ahora el valor predeterminado para el compilador es /Zp8, lo que significa que cada estructura está alineada al menos a 8 bytes, por lo que no habría necesidad de corregir la alineación del "primer" miembro en este caso de todos modos.

Saludos, Sharm

1

sospecho que this answer tiene algo que ver con ello. Para citar de respuesta dirkgently 's, citando el manual de GCC:

Tenga en cuenta que la alineación de cualquier tipo de estructura o unión dada es requerido por la norma ISO C a ser por lo menos un múltiplo perfecto del mínimo común múltiplo de las alineaciones de todos los miembros de la estructura o unión en cuestión.

De acuerdo con esa regla, una vez que haya agregado un doble de 8 bytes, el compilador tiene que organizar todo en múltiplos de 8 bytes. Puede anular eso con #pragma pack(), por supuesto, aunque será menos eficiente si termina con un miembro de 8 bytes en algo que no sea un límite de 8 bytes.

+1

No, no es así. Lo único que dice la cita es que, debido al miembro 'double' *, toda la estructura * debe alinearse en un límite de 8 bytes. Pero no explica por qué cambió la alineación del miembro entero. Esta estructura debe caber en 16 bytes con todos los requisitos de alineamiento perfectamente. Pero el compilador produjo 24. ¿Por qué? – AnT

+0

Curiosamente, esta página de ejemplos de alineación para el código x64 en VC++ 8 muestra lo mismo con una estructura que contiene un int y un doble, pero lamentablemente no dice por qué. http://msdn.microsoft.com/en-us/library/71kf49f1(VS.80).aspx Claramente, el compilador elige alinear los miembros en el denominador común más bajo, que es 8, por lo tanto, la necesidad de la alineación. Probablemente espacio para la interpretación en esa oración. – aalpern

+0

aaplem - ese enlace muestra lo mismo con un int y un doble. Eso es probablemente porque quieres que los dobles estén alineados a 8 bytes. Siempre pensé que tener dobles alineados a 4 bytes es super lento en x86. – Mason

2

This blog post analiza el mismo problema e incluye una explicación en los comentarios de Jan Gray, quien escribió el código de diseño del compilador MS C++ hace mucho tiempo.

Para parafrasear, el vfptr se inserta en el diseño de clase solo después de que los otros miembros de datos se hayan distribuido.Dependiendo de los requisitos de alineación de los miembros de datos, esto significa que puede introducirse un relleno innecesario. Además, esto solo ocurre si la clase no tiene una clase base con un vfptr.

ahí la solución, que se presenta en la entrada del blog:

class EmptyBase 
{ 
protected: 
    virtual ~EmptyBase() {} 
}; 

class Something : public EmptyBase {  
    virtual void sampleVirtualMethod(); 
    int someInteger; 
    **double someDouble;** 
}; 

sizeof(Something) debería ser 16 en este caso.