una sirve que para lenguaje imprimir estructuras estructura entre ejercicios diferencia datos como arreglo anidadas c++ data-structures

sirve - ¿Este truco de inicialización de la estructura C++ es seguro?



estructuras en c++ pdf (16)

En lugar de tener que recordar inicializar una simple estructura ''C'', podría derivar de ella y ponerla en cero en el constructor de esta manera:

struct MY_STRUCT { int n1; int n2; }; class CMyStruct : public MY_STRUCT { public: CMyStruct() { memset(this, 0, sizeof(MY_STRUCT)); } };

Este truco a menudo se usa para inicializar estructuras Win32 y algunas veces puede configurar el miembro cbSize omnipresente.

Ahora, mientras no haya una tabla de funciones virtual para la llamada del memset para destruir, ¿es esto una práctica segura?


PREÁMBULO:

Si bien mi respuesta todavía está bien, la respuesta de litb es bastante superior a la mía porque:

  1. Me enseña un truco que no sabía (las respuestas de litb generalmente tienen este efecto, pero esta es la primera vez que lo escribo)
  2. Responde exactamente la pregunta (es decir, inicializar la parte de la estructura original a cero)

Entonces, por favor, considera la respuesta de Litb antes que la mía. De hecho, sugiero que el autor de la pregunta considere la respuesta de litb como la correcta.

Respuesta original

Poner un objeto verdadero (es decir, std :: string) etc. dentro se romperá, porque el objeto verdadero se inicializará antes que el memset, y luego, se sobrescribirá con ceros.

Usar la lista de inicialización no funciona para g ++ (estoy sorprendido ...). Inicialícela en su lugar en el cuerpo constructor CMyStruct. Será amigable con C ++:

class CMyStruct : public MY_STRUCT { public: CMyStruct() { n1 = 0 ; n2 = 0 ; } };

PD: Supuse que no tienes control sobre MY_STRUCT, por supuesto. Con control, habría agregado el constructor directamente dentro de MY_STRUCT y olvidado sobre la herencia. Tenga en cuenta que puede agregar métodos no virtuales a una estructura tipo C, y aún hacer que se comporte como una estructura.

EDITAR: Se han agregado paréntesis faltantes, después del comentario de Lou Franco. ¡Gracias!

EDIT 2: Probé el código en g ++, y por alguna razón, el uso de la lista de inicialización no funciona. Corregí el código usando el constructor del cuerpo. La solución aún es válida, sin embargo.

Por favor reevaluar mi publicación, ya que el código original fue cambiado (ver changelog para más información).

EDIT 3: Después de leer el comentario de Rob, creo que tiene un punto digno de discusión: "De acuerdo, pero esta podría ser una enorme estructura de Win32 que puede cambiar con un nuevo SDK, por lo que un memset es una prueba de futuro".

No estoy de acuerdo: conocer a Microsoft no cambiará debido a su necesidad de una perfecta compatibilidad con versiones anteriores. En su lugar, crearán una estructura extendida MY_STRUCT con el mismo diseño inicial que MY_STRUCT, con miembros adicionales al final, y reconocible a través de una variable de miembro de "tamaño" como la estructura utilizada para RegisterWindow, IIRC.

Entonces, el único punto válido que queda del comentario de Rob es la estructura "enorme". En este caso, quizás un memset sea más conveniente, pero deberá hacer que MY_STRUCT sea un miembro variable de CMyStruct en lugar de heredarlo.

Veo otro truco, pero supongo que esto se romperá debido a un posible problema de alineación estructural.

EDIT 4: Por favor, eche un vistazo a la solución de Frank Krueger. No puedo prometer que sea portable (supongo que sí), pero sigue siendo interesante desde un punto de vista técnico porque muestra un caso en el que, en C ++, la "dirección" del puntero "this" pasa de su clase base a su clase heredada .


Comenta la respuesta de litb (parece que todavía no puedo comentar directamente):

Incluso con esta buena solución de estilo C ++ debes tener mucho cuidado de no aplicar esto ingenuamente a una estructura que contenga un miembro que no sea POD.

Algunos compiladores ya no se inicializan correctamente.

Vea esta respuesta a una pregunta similar . Personalmente tuve la mala experiencia en VC2008 con una std :: string adicional.


Desde un punto de vista ISO C ++, hay dos problemas:

(1) ¿Es el objeto un POD? El acrónimo significa Plain Old Data, y el estándar enumera lo que no se puede tener en un POD (Wikipedia tiene un buen resumen). Si no es un POD, no lo puedes configurar.

(2) ¿Hay miembros para los que todos los bits cero no son válidos? En Windows y Unix, el puntero NULL es todos los bits cero; no necesita ser El punto flotante 0 tiene todos los bits cero en IEEE754, que es bastante común, y en x86.

La sugerencia de Frank Kruegers aborda sus preocupaciones al restringir el memset a la base POD de la clase que no pertenece a POD.


El diseño preciso de una clase o estructura no está garantizado en C ++, por lo que no debes hacer suposiciones sobre el tamaño desde el exterior (eso significa que no eres un compilador).

Probablemente funcione, hasta que encuentre un compilador en el que no lo haga, o le arroje algún vtable a la mezcla.


Es un poco de código, pero es reutilizable; incluirlo una vez y debería funcionar para cualquier POD. Puede pasar una instancia de esta clase a cualquier función esperando un MY_STRUCT, o usar la función GetPointer para pasarla a una función que modificará la estructura.

template <typename STR> class CStructWrapper { private: STR MyStruct; public: CStructWrapper() { STR temp = {}; MyStruct = temp;} CStructWrapper(const STR &myStruct) : MyStruct(myStruct) {} operator STR &() { return MyStruct; } operator const STR &() const { return MyStruct; } STR *GetPointer() { return &MyStruct; } }; CStructWrapper<MY_STRUCT> myStruct; CStructWrapper<ANOTHER_STRUCT> anotherStruct;

De esta forma, no tiene que preocuparse de si los NULL son todos 0, o las representaciones de coma flotante. Siempre que STR sea un tipo agregado simple, las cosas funcionarán. Cuando STR no es un tipo agregado simple, obtendrá un error en tiempo de compilación, por lo que no tendrá que preocuparse por utilizarlo de forma accidental. Además, si el tipo contiene algo más complejo, siempre que tenga un constructor predeterminado, estás bien:

struct MY_STRUCT2 { int n1; std::string s1; }; CStructWrapper<MY_STRUCT2> myStruct2; // n1 is set to 0, s1 is set to "";

En el lado negativo, es más lento ya que está haciendo una copia temporal adicional, y el compilador asignará a cada miembro a 0 individualmente, en lugar de a un memset.


Este es un ejemplo perfecto de portar un modismo C a C ++ (y por qué podría no funcionar siempre ...)

El problema que tendrá con el uso de memset es que en C ++, una estructura y una clase son exactamente lo mismo, excepto que, de forma predeterminada, una estructura tiene visibilidad pública y una clase tiene visibilidad privada.

Por lo tanto, ¿qué pasa si más adelante, algún programador bien intencionado cambia MY_STRUCT de la siguiente manera:

struct MY_STRUCT { int n1; int n2; // Provide a default implementation... virtual int add() {return n1 + n2;} };

Al agregar esa función única, su memset ahora podría causar estragos. Hay una discusión detallada en comp.lang.c+


Esto me haría sentir mucho más seguro ya que debería funcionar incluso si hay un vtable (o el compilador gritará).

memset(static_cast<MY_STRUCT*>(this), 0, sizeof(MY_STRUCT));

Estoy seguro de que su solución funcionará, pero dudo que haya alguna garantía que se pueda hacer al mezclar memset y las clases.


Lo que hago es usar la inicialización agregada, pero solo especificar inicializadores para los miembros que me interesan, por ejemplo:

STARTUPINFO si = { sizeof si, /*cb*/ 0, /*lpReserved*/ 0, /*lpDesktop*/ "my window" /*lpTitle*/ };

Los miembros restantes se inicializarán en ceros del tipo apropiado (como en la publicación de Drealmer). Aquí, confías en que Microsoft no rompa la compatibilidad de forma gratuita al agregar nuevos miembros de la estructura en el medio (una suposición razonable). Esta solución me parece óptima: una afirmación, sin clases, sin memset, sin suposiciones sobre la representación interna del punto flotante cero o punteros nulos.

Creo que los hacks relacionados con la herencia son de estilo horrible. La herencia pública significa IS-A para la mayoría de los lectores. Tenga en cuenta también que está heredando de una clase que no está diseñada para ser una base. Como no existe un destructor virtual, los clientes que eliminen una instancia de clase derivada a través de un puntero a la base invocarán un comportamiento no definido.


Los ejemplos tienen "comportamiento no especificado".

Para un no-POD, el orden por el cual el compilador establece un objeto (todas las clases base y los miembros) no está especificado (ISO C ++ 10/3). Considera lo siguiente:

struct A { int i; }; class B : public A { // ''B'' is not a POD public: B (); private: int j; };

Esto se puede presentar como:

[ int i ][ int j ]

O como:

[ int j ][ int i ]

Por lo tanto, usar memset directamente en la dirección de ''esto'' es un comportamiento muy no especificado. Una de las respuestas anteriores, a primera vista, parece ser más segura:

memset(static_cast<MY_STRUCT*>(this), 0, sizeof(MY_STRUCT));

Creo, sin embargo, que hablando estrictamente esto también da como resultado un comportamiento no especificado. No puedo encontrar el texto normativo, sin embargo, la nota en 10/5 dice: "Un subobjeto de clase base puede tener un diseño (3.7) diferente del diseño de un objeto más derivado del mismo tipo".

Como resultado, el compilador podría realizar optimizaciones de espacio con los diferentes miembros:

struct A { char c1; }; struct B { char c2; char c3; char c4; int i; }; class C : public A, public B { public: C () : c1 (10); { memset(static_cast<B*>(this), 0, sizeof(B)); } };

Puede ser presentado como:

[ char c1 ] [ char c2, char c3, char c4, int i ]

En un sistema de 32 bits, debido a alighments, etc. para ''B'', sizeof (B) probablemente sea de 8 bytes. Sin embargo, sizeof (C) también puede ser ''8'' bytes si el compilador empaqueta los miembros de datos. Por lo tanto, la llamada a memset podría sobrescribir el valor dado a ''c1''.


Mi opinión es no. No estoy seguro de lo que gana tampoco.

A medida que su definición de CMyStruct cambia y agrega / elimina miembros, esto puede provocar errores. Fácilmente.

Crea un constructor para CMyStruct que tome un MyStruct tiene un parámetro.

CMyStruct::CMyStruct(MyStruct &)

O algo de eso buscado. A continuación, puede inicializar un miembro ''MyStruct'' público o privado.


Mucho mejor que un memset, puedes usar este pequeño truco en su lugar:

MY_STRUCT foo = { 0 };

Esto inicializará todos los miembros a 0 (o su valor predeterminado iirc), sin necesidad de especificar un valor para cada uno.


Prueba esto - sobrecarga nuevo.

EDITAR: Debería añadir: esto es seguro porque la memoria se pone a cero antes de llamar a los constructores. Gran falla: solo funciona si el objeto se asigna dinámicamente.

struct MY_STRUCT { int n1; int n2; }; class CMyStruct : public MY_STRUCT { public: CMyStruct() { // whatever } void* new(size_t size) { // dangerous return memset(malloc(size),0,size); // better if (void *p = malloc(size)) { return (memset(p, 0, size)); } else { throw bad_alloc(); } } void delete(void *p, size_t size) { free(p); } };


Si MY_STRUCT es su código y está contento con el uso de un compilador de C ++, puede colocar el constructor allí sin ajustar en una clase:

struct MY_STRUCT { int n1; int n2; MY_STRUCT(): n1(0), n2(0) {} };

No estoy seguro acerca de la eficiencia, pero odio hacer trucos cuando no se ha demostrado que se necesita eficiencia.


Si ya tiene un constructor, ¿por qué no inicializarlo allí con n1 = 0? n2 = 0; - Esa es ciertamente la forma más normal .

Editar: en realidad, como ha demostrado paercebal, la inicialización de ctor es aún mejor.


Simplemente puede inicializar el valor de la base y todos sus miembros serán eliminados por cero. Esto está garantizado

struct MY_STRUCT { int n1; int n2; }; class CMyStruct : public MY_STRUCT { public: CMyStruct():MY_STRUCT() { } };

Para que esto funcione, no debería haber un constructor declarado por el usuario en la clase base, como en su ejemplo.

No memset desagradable para eso. No se garantiza que memset funcione en tu código, aunque debería funcionar en la práctica.


Supongo que la estructura se te proporciona y no se puede modificar. Si puede cambiar la estructura, entonces la solución obvia es agregar un constructor.

No sobreescriba su código con envoltorios de C ++ cuando todo lo que quiere es una macro simple para inicializar su estructura.

#include <stdio.h> #define MY_STRUCT(x) MY_STRUCT x = {0} struct MY_STRUCT { int n1; int n2; }; int main(int argc, char *argv[]) { MY_STRUCT(s); printf("n1(%d),n2(%d)/n", s.n1, s.n2); return 0; }