2008-12-19 7 views
6

Dada una variable foo de tipo FooClass* y una variable miembro en el que la clase llamada bar, es la distancia entre foo y &(foo->bar) el mismo en cualquier situación con algunas limitaciones:Expertos en C++: ¿el desplazamiento de una variable miembro a su constante de clase en estas condiciones?

  1. FooClass es un tipo no-POD.

  2. Sabemos que foo siempre apuntará a una instancia de FooClass, y no a algún subtipo de la misma.

  3. Solo nos importa el comportamiento en un único compilador y una sola compilación; es decir, el valor que esto puede dar como resultado bajo gcc nunca se usa en el código compilado con MSVC, y nunca se guarda para ser reutilizado entre compilaciones. Se calcula en el binario y se usa en el binario, y eso es todo.

  4. No usamos un new personalizado, aunque algunas instancias de la clase pueden ser asignadas en pila y algunas asignadas en el montón.

  5. No hay ctor explícito para FooClass; se basa en el generado por el compilador (y cada uno de los campos en FooClass es POD o constructable por defecto).

no puedo encontrar una garantía de cualquier manera sobre esto en el estándar (ni tampoco espero), pero mi prueba rudimentaria con gcc me lleva a creer que siempre será el caso. También sé que esta garantía está hecha para tipos POD, pero supongamos que este tipo no puede ser POD.

Una actualización/aclaración: esto es solo para una sola compilación de un solo binario; los desplazamientos calculados nunca saldrán de esa única ejecución. Básicamente, quiero poder identificar de forma única los campos de una clase en un mapa estático y luego poder buscar en ese mapa algún truco de macro/plantilla/MAL. Es solo para mi propia diversión, y ninguna máquina de soporte de vida dependerá de este código.

Respuesta

5

Después de haber compilado su programa, Sí *.

El desplazamiento se mantendrá constante.Sin embargo, hay una restricción muy importante: foo debe apuntar específicamente a un objeto FooClass. No es una clase derivada de FooClass, ni nada por el estilo. El motivo por el cual C++ hace la distinción POD con respecto a las compensaciones de miembros es porque tanto la herencia múltiple como la ubicación (o falta de) un puntero vtable pueden crear situaciones donde la dirección de un objeto no es la misma que la dirección de ese objeto base.

3

No soy un experto, pero me va a intentar responder de todos modos :)

  1. FooClass es un tipo no-POD. Esto significa que podría tener más de una sección de private, public o protected. Dentro de dicha sección, el orden es el de la definición de los miembros, pero a través de esas secciones, el orden es arbitrario y no especificado.
  2. foo siempre apunta a FooClass. Bueno, así que tenemos garantía de que no hay un ajuste de compensación hecho. Al menos en una compilación, las compensaciones serán las mismas (no tienen la cotización estándar de respaldo. Pero no puede funcionar si fueran diferentes).
  3. Solo nos importa el comportamiento en un solo compilador. Bien, dado que el orden de los miembros no se especifica en las secciones de modificadores de acceso y el compilador tiene permiso para colocar el relleno entre los miembros, esto no nos comprará mucho.
  4. Solo nos importan los objetos en la pila (duración de almacenamiento automático). Bueno, no veo cómo eso cambia nada del diseño del objeto.

Así que, después de todo, no creo que tenga ninguna garantía de que el desplazamiento sea constante en las compilaciones.Para consideraciones dentro de una compilación (por lo tanto, si jugáramos con un compilador cuyo código generado utiliza un ABI que cambia con cada compilación diferente), el desplazamiento no puede ser diferente. Pero incluso si conoce el desplazamiento, no puede acceder al miembro. La única forma de acceder a un miembro es utilizando el operador de acceso de miembro -> y . (eso se dice en 9.2/9).

¿Por qué no utilizar punteros de miembros de datos? Permiten acceder a los miembros de forma segura. Aquí hay un ejemplo: (looking up members by name).

+0

Quiero identificar de manera única a los miembros de modo que pueda ponerlos en un "conjunto" de géneros e iterar sobre ellos. Implica plantillas y macros, por lo que debe ser automático (no puedo simplemente hacerlo en el ctor por nombre). Mi hack actual funciona, ¡pero gracias por la publicación informativa! – hazzen

+0

Puede ordenar por miembros utilizando punteros de datos de miembros también –

+0

Estoy haciendo algo similar (fooOptions basadas en macros estructura para construir objetos Foo que necesitarían muchos parámetros para configurar, algunos opcionales y otros obligatorios), y lo estaba intentando como pocas pulsaciones de teclas como sea posible: la forma enlazada funcionaría también si quisiera escribir más. – hazzen

1

hay dos cosas de la parte superior de la cabeza que afectan a la distribución interna de un objeto:

  • el tamaño de los objetos miembros. Con suerte, volverá a compilar todos los módulos afectados si cambia una definición de miembro.
  • pragmas de embalaje pueden cambiar el relleno agregado entre los miembros. Asegúrese de que el empaque sea el mismo para todos los módulos que usan la clase, o al menos la sección donde se define la clase. Si no lo haces, tendrás problemas más grandes que las compensaciones impredecibles.
+0

Si la variable miembro es un puntero, ¿cómo afecta el tamaño del objeto miembro? Seguramente, lo único que importará es el modelo ILP (entero, largo, puntero) para el compilador/plataforma en el que está trabajando. –

+0

Dije objetos miembros, no punteros de miembros. Si amplía la definición de "objeto" para incluir tipos de POD incluyendo punteros, la afirmación aún se mantiene; siempre que el tamaño del puntero (no el tamaño del objeto puntiagudo) no cambie, está bien. –

1

Conclusión: si esta clase contiene algo más que POD, entonces no puede hacer ninguna suposición acerca de los desplazamientos. Si la clase es solo una colección de POD públicas, entonces estás a salvo.

Aquí hay un enlace a una parte de un capítulo en un excelente libro intermedio de C++. Recomiendo a todos que lean este libro si hablan en serio sobre C++.

Este extracto particular, se dirige a una parte de la cuestión que aquí se presenta:

http://my.safaribooksonline.com/0321321928/ch11?portal=oreilly

Para el resto de los detalles, echa un vistazo al libro. Mi "línea inferior" de arriba es un resumen simplista de este capítulo.

3

En un único compilador donde la configuración del compilador es siempre la misma y FooClass no agrega ni quita nada, entonces sí, la distancia entre la dirección almacenada en foo y &(foo->bar) siempre será la misma, o el compilador no sería capaz de generar el código adecuado que funcionó en las unidades de compilación.

Sin embargo, una vez que agrega algo a la clase o cambia la configuración del compilador, todas las apuestas están desactivadas.

3

Hasta donde yo sé, este debería ser siempre el caso, clase POD o no. En tiempo de compilación, según el compilador, la arquitectura, la configuración, etc., el compilador determina el tamaño de la clase y los desplazamientos de todos sus miembros. Esto se corrige para todas las instancias de la clase en la unidad de compilación (y por extensión, la unidad vinculada, si se conserva la regla de una sola definición).

Desde los compilador trata tipo punteros, literalmente, incluso si el tipo subyacente es incorrecta (por ejemplo: el puntero ha sido c-estilo elenco incorrectamente), la distancia calculada entre & foo y & (foo.bar) será el mismo , ya que el desplazamiento se conoce estáticamente en el momento de la compilación.

Nota: Esto definitivamente se ha hecho antes, efectivamente. Véase, por ejemplo, los datos de Microsoft ATL código mediante su unión a 'offsetof' macro ...

+0

offsetoff se define en el estándar para solo tomar tipos/uniones POD (sección 18.1) – hazzen

+0

La macro funciona igual de bien para los tipos que no son POD; He usado construcciones similares. Son particularmente útiles para el mapeo de enlaces de datos para mecanismos de acceso a la base de datos (lo mismo para lo que MS lo usa). – Nick

+0

no funciona para tipos que no sean POD. Es solo para tipos de POD. –

1

Sí, el desplazamiento se determina en tiempo de compilación, por lo que siempre que no se comparen los desplazamientos entre compilaciones o compiladores, siempre será constante.

Cuestiones relacionadas