Forma adecuada de inicializar estructuras C ++

82

Nuestro código involucra una estructura POD (Plain Old Datastructure) (es una estructura c ++ básica que tiene otras estructuras y variables POD que deben inicializarse al principio).

Basado en lo que he leído , parece que:

myStruct = (MyStruct*)calloc(1, sizeof(MyStruct));

debería inicializar todos los valores a cero, al igual que:

myStruct = new MyStruct();

Sin embargo, cuando la estructura se inicializa de la segunda forma, Valgrind luego se queja de que "el salto o movimiento condicional depende de valores no inicializados" cuando se utilizan esas variables. ¿Mi comprensión es defectuosa aquí, o Valgrind arroja falsos positivos?

Sombra503
fuente
1
Además, tenga en cuenta que new MyStruct()no fue necesario establecer bytes de relleno en C ++ 03. En C ++ 0x lo es. Los bits de relleno se establecerán en 0 en C ++ 0x.
Johannes Schaub - litb
Gracias por la captura, edité mi pregunta.
Shadow503
Preguntas frecuentes publicadas: stackoverflow.com/questions/620137/…
Robᵩ
Según tengo entendido, no debería recibir ese mensaje de error. ¿Quizás su estructura de datos no es un POD? ¿Puede reducir su código al programa más pequeño que aún demuestra este comportamiento y publicar ese programa destilado? (Ver sscce.org ).
Robᵩ
¿Qué compilador? ¿Puedes publicar la definición de MyStruct?
Alan Stokes

Respuestas:

120

En C ++, las clases / estructuras son idénticas (en términos de inicialización).

Una estructura que no sea POD también puede tener un constructor para que pueda inicializar miembros.
Si su estructura es un POD, entonces puede usar un inicializador.

struct C
{
    int x; 
    int y;
};

C  c = {0}; // Zero initialize POD

Alternativamente, puede usar el constructor predeterminado.

C  c = C();      // Zero initialize using default constructor
C  c{};          // Latest versions accept this syntax.
C* c = new C();  // Zero initialize a dynamically allocated object.

// Note the difference between the above and the initialize version of the constructor.
// Note: All above comments apply to POD structures.
C  c;            // members are random
C* c = new C;    // members are random (more officially undefined).

Creo que valgrind se queja porque así es como solía funcionar C ++. (No estoy exactamente seguro de cuándo se actualizó C ++ con la construcción predeterminada de inicialización cero). Su mejor opción es agregar un constructor que inicialice el objeto (las estructuras son constructores permitidos).

Como nota al margen:
muchos principiantes intentan valorar init:

C c(); // Unfortunately this is not a variable declaration.
C c{}; // This syntax was added to overcome this confusion.

// The correct way to do this is:
C c = C();

Una búsqueda rápida del "análisis más irritante" proporcionará una mejor explicación que yo.

Martin York
fuente
1
¿Tiene una referencia para esta inicialización cero de variables en C ++? Lo último que escuché es que todavía tiene el mismo comportamiento que C.IIRC, el compilador de MSVC incluso eliminó miembros no inicializados de inicialización cero en algún momento (por razones de rendimiento, supongo), lo que aparentemente creó problemas bastante graves para las otras divisiones de MS :)
Marc Mutz - mmutz
1
no, el C(), pero parece que tienes razón .
Marc Mutz - mmutz
4
Con una clase POD, T, T()tiene siempre cero inicializan los subobjetos escalares. El nombre de la inicialización de nivel superior se llamó de manera diferente en C ++ 98 que en C ++ 03 ("inicialización predeterminada" frente a "inicialización de valor"), pero el efecto final para los POD es el mismo. Tanto C ++ 98 como C ++ 03 tendrían todos los valores en cero.
Johannes Schaub - litb
1
@RockyRaccoon C c();desafortunadamente no es una declaración de variable sino más bien una declaración hacia adelante de una función (una función llamada 'c' que no toma parámetros y devuelve un objeto de tipo C). Google: análisis más irritante
Martin York
1
@RockyRaccoon La diferencia aquí y con la pregunta a la que enlazas en tu comentario; es que en este caso no hay parámetros usados ​​en el constructor. Por lo tanto, el compilador tiene dificultades para analizar esto y diferenciar entre la declaración de variable y la declaración directa de una función. Elige la declaración de función de avance. Si necesita un parámetro en el constructor, el compilador puede ver que se trata de una declaración de variable sin ningún problema. C c(4);Es una declaración de variable válida.
Martin York
2

Por lo que nos ha dicho, parece ser un falso positivo en valgrind. La newsintaxis con ()debe inicializar el valor del objeto, asumiendo que es POD.

¿Es posible que alguna subparte de su estructura no sea realmente POD y eso impida la inicialización esperada? ¿Puede simplificar su código en un ejemplo postable que aún marca el error valgrind?

Alternativamente, quizás su compilador no inicialice realmente las estructuras POD.

En cualquier caso, probablemente la solución más simple es escribir constructor (es) según sea necesario para la estructura / subpartes.

Marca B
fuente
1

Escribo un código de prueba:

#include <string>
#include <iostream>
#include <stdio.h>

using namespace std;

struct sc {
    int x;
    string y;
    int* z;
};

int main(int argc, char** argv)
{
   int* r = new int[128];
   for(int i = 0; i < 128; i++ ) {
        r[i] = i+32;
   }
   cout << r[100] << endl;
   delete r;

   sc* a = new sc;
   sc* aa = new sc[2];
   sc* b = new sc();
   sc* ba = new sc[2]();

   cout << "az:" << a->z << endl;
   cout << "bz:" << b->z << endl;
   cout << "a:" << a->x << " y" << a->y << "end" << endl;
   cout << "b:" << b->x << " y" << b->y <<  "end" <<endl;
   cout << "aa:" << aa->x << " y" << aa->y <<  "end" <<endl;
   cout << "ba:" << ba->x << " y" << ba->y <<  "end" <<endl;
}

g ++ compila y ejecuta:

./a.out 
132
az:0x2b0000002a
bz:0
a:854191480 yend
b:0 yend
aa:854190968 yend
ba:0 yend
Yadong
fuente
Ok, no estoy tan familiarizado con new sc;vs new sc();. Habría pensado que los parens faltantes eran un error. Pero esto parece demostrar que la respuesta es correcta (que no init a 0 cuando se utiliza el constructor predeterminado.)
nycynik
0

Necesita inicializar los miembros que tenga en su estructura, por ejemplo:

struct MyStruct {
  private:
    int someInt_;
    float someFloat_;

  public:
    MyStruct(): someInt_(0), someFloat_(1.0) {} // Initializer list will set appropriate values

};
ralphtheninja
fuente
En este caso, no estoy usando una clase, sino una estructura.
Shadow503
Una estructura es una clase. Editado mi publicación :)
ralphtheninja
2
No es necesario hacer esto para un POD
Alan Stokes
0

Dado que es una estructura POD, siempre puede configurarlo en 0; esta podría ser la forma más fácil de inicializar los campos (suponiendo que sea apropiado).

Scott C Wilson
fuente
1
Ya sea una estructura o una clase, no tiene nada que ver con la capacidad de crear memset o crear un constructor. Ver, por ejemplo, stackoverflow.com/questions/2750270/cc-struct-vs-class
Erik
Normalmente, no establecería una clase después de crearla, ya que su constructor (presumiblemente) hizo la inicialización.
Scott C Wilson
1
no hay diferencia entre "clase" y "estructura" en C ++, excepto por la protección predeterminada (privada y pública, respectivamente). Una estructura puede ser una no POD y una clase puede ser una POD, estos conceptos son ortogonales.
Marc Mutz - mmutz
@mmutz y @Erik - estuvieron de acuerdo - acortaron mi respuesta para evitar confusiones.
Scott C Wilson
memset () no es la mejor opción, puede inicializar a cero una estructura POD simplemente llamando a su ctor predeterminado.
Nils
0

Esa me parece la forma más fácil. Los miembros de la estructura se pueden inicializar usando llaves '{}'. Por ejemplo, a continuación se muestra una inicialización válida.

struct Point 
{ 
   int x, y; 
};  

int main() 
{ 
   // A valid initialization. member x gets value 0 and y 
   // gets value 1.  The order of declaration is followed. 
   struct Point p1 = {0, 1};  
}

Hay buena información sobre estructuras en c ++: https://www.geeksforgeeks.org/structures-in-cpp/

Michael Klishevich
fuente