23 votos

¿Por qué puede ser peligroso utilizar este POD struct como una clase base?

He tenido esta conversación con un colega, y resultó ser muy interesante. Dicen que tenemos la siguiente clase POD

struct A { 
  void clear() { memset(this, 0, sizeof(A)); } 

  int age; 
  char type; 
};

clear se destina para borrar todos los miembros, el establecimiento de a 0 (byte). ¿Qué podría salir mal si utilizamos A como clase base? Hay una sutil fuente de errores aquí.

17voto

StackedCrooked Puntos 12247

El compilador es probable que añadir bytes de relleno para A. por Lo sizeof(A) se extiende más allá de char type (hasta el final del relleno). Sin embargo, en el caso de la herencia que el compilador no podría agregar el collar de bytes. Por lo que la llamada a memset sobrescribirá parte de la subclase.

4voto

Karl Knechtel Puntos 24349

En adición a las otras notas, sizeof es tiempo de compilación del operador, de manera clear() no de cero adicional a sus miembros por las clases derivadas (excepto como se indica debido a relleno de forma extraña).

No hay nada realmente "sutil" acerca de esto; memset es una cosa horrible para usar en C++. En los raros casos en los que realmente sólo pueden llenar la memoria con ceros y esperar que sane comportamiento, y que realmente se necesitan para llenar la memoria con ceros, y cero inicializar todo a través de la lista de inicializador de la manera civilizada de alguna manera es inaceptable, uso std::fill lugar.

2voto

Adam Rosenfield Puntos 176408

En teoría, el compilador puede diseñar la base de las clases de manera diferente. C++03 §10 párrafo 5 dice:

Una clase base subobjeto podría tener un diseño (3.7) diferente de la disposición de la mayoría de los derivados del objeto del mismo tipo.

Como StackedCrooked mencionado, esto puede suceder por el compilador de la adición de relleno al final de la clase base A cuando existe como su propio objeto, pero el compilador no podría agregar que el relleno cuando se trata de una clase base. Esto causaría A::clear() sobrescribir los primeros bytes de los miembros de la subclase.

Sin embargo, en la práctica, no he sido capaz de conseguir que esto suceda, ya sea con GCC o Visual Studio 2008. El uso de esta prueba:

struct A
{
  void clear() { memset(this, 0, sizeof(A)); }

  int age;
  char type;
};

struct B : public A
{
  char x;
};

int main(void)
{
  B b;
  printf("%d %d %d\n", sizeof(A), sizeof(B), ((char*)&b.x - (char*)&b));
  b.x = 3;
  b.clear();
  printf("%d\n", b.x);

  return 0;
}

Y modificando A, B, o ambos a ser 'lleno' (con #pragma pack en VS y __attribute__((packed)) en GCC), yo no podía conseguir b.x se sobrescriben en cualquier caso. Las optimizaciones se han habilitado. Los 3 valores impresos para los tamaños/compensaciones fueron siempre 8/12/8, 8/9/8, o 5/6/5.

0voto

Thomas Matthews Puntos 19838

El clear método de la clase base sólo de establecer los valores de los miembros de la clase.

De acuerdo a la alineación de las reglas, se permite el compilador para insertar el relleno de manera que el siguiente miembro de datos se producen en el alineado de la frontera. Por lo tanto habrá relleno después de la type miembro de datos. El primer miembro de datos de la descendiente va a ocupar esta ranura y estar libre de los efectos de la memset, ya que el sizeof la clase base no incluye el tamaño de la descendencia. El tamaño de los padres != la talla del niño (a menos que el niño no tiene los datos de los miembros). Ver rebanar.

Embalaje de las estructuras no es una parte de la lengua estándar. Esperemos que, con un buen compilador, el tamaño de un pic estructura no incluye ningún extra bytes después de la última. Aún así, una de pic descendiente hereda de una pic de los padres debe producir el mismo resultado: los padres de los conjuntos sólo a los miembros de datos en el padre.

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X