¿Cómo inicializar la memoria con un nuevo operador en C ++?

176

Estoy empezando a entrar en C ++ y quiero aprender algunos buenos hábitos. Si acabo de asignar una matriz de tipos intcon el newoperador, ¿cómo puedo inicializarlos a 0 sin recorrerlos todos yo mismo? ¿Debo usar memset? ¿Hay una manera "C ++" de hacerlo?

dreamlax
fuente
19
Si desea adquirir un buen hábito de C ++, evite usar matrices directamente y use vector en su lugar. Vector inicializará todos los elementos independientemente del tipo, y luego no necesita recordar llamar al operador delete [].
brianegge
@brianegge: ¿Qué pasa si necesito pasar una matriz a una función C externa, puedo darle el vector?
dreamlax
12
Puedes pasar &vector[0].
jamesdlin
Por supuesto, cuando pasa matrices a funciones C, normalmente tiene que especificar el puntero al primer elemento, & vector [0] como dijo @jamesdlin, y el tamaño de la matriz, proporcionado en este caso por vector.size ().
Trebor Rude
Relacionado (pregunta por tipos que no son de matriz): stackoverflow.com/questions/7546620/…
Aconcagua

Respuestas:

392

Es una característica sorprendentemente poco conocida de C ++ (como lo demuestra el hecho de que nadie ha dado esto como respuesta todavía), pero en realidad tiene una sintaxis especial para inicializar un conjunto de valores:

new int[10]();

Tenga en cuenta que debe usar los paréntesis vacíos; no puede, por ejemplo, usar (0)ni nada más (por lo que esto solo es útil para la inicialización de valores).

Esto está explícitamente permitido por ISO C ++ 03 5.3.4 [expr.new] / 15, que dice:

Una nueva expresión que crea un objeto de tipo Tinicializa ese objeto de la siguiente manera:

...

  • Si el nuevo inicializador tiene la forma (), el elemento tiene un valor inicializado (8.5);

y no restringe los tipos para los que está permitido, mientras que el (expression-list)formulario está restringido explícitamente por otras reglas en la misma sección, de modo que no permite tipos de matriz.

Pavel Minaev
fuente
1
Si bien estoy de acuerdo en que esto es poco conocido, no puedo (totalmente) estar de acuerdo en que sea realmente muy sorprendente: se agregó en C ++ 03, que la mayoría de las personas parecen haber ignorado (ya que esta fue una de las pocas cosas nuevas sí agregó).
Jerry Coffin
2
@Jerry: Debo admitir que aún no lo sabía (probablemente porque cuando pude leer el estándar, ya era C ++ 03). Dicho esto, es notable que todas las implementaciones que conozco respalden esto (supongo que es porque es muy trivial de implementar).
Pavel Minaev
2
Sí, es bastante trivial de implementar. En cuanto a ser nuevo, toda la "inicialización de valor" era nueva en C ++ 03.
Jerry Coffin
34
En C ++ 11 puede utilizar initializtion uniforme, así: new int[10] {}. También puede proporcionar valores para inicializar con:new int[10] {1,2,3}
bames53
No confunda default-initialized con value-initialized: ambos están claramente definidos en el estándar y son diferentes inicializaciones.
Deduplicador
25

Suponiendo que realmente desea una matriz y no un std :: vector, la "forma C ++" sería esta

#include <algorithm> 

int* array = new int[n]; // Assuming "n" is a pre-existing variable

std::fill_n(array, n, 0); 

Pero tenga en cuenta que, en realidad, esto sigue siendo en realidad solo un bucle que asigna cada elemento a 0 (realmente no hay otra forma de hacerlo, salvo una arquitectura especial con soporte a nivel de hardware).

Tyler McHenry
fuente
No me importa si el bucle se implementa debajo de una función, solo quería saber si tuve que implementar o no ese bucle. Gracias por el consejo.
dreamlax
44
Te sorprenderías. Yo era. En mi STL (GCC y Dinkumware), std :: copy en realidad se convierte en una memoria si detecta que se llama con tipos incorporados. No me sorprendería si std :: fill_n usara memset.
Brian Neal
2
No Use 'Value-Initialization' para establecer todos los miembros en 0.
Martin York
24

Hay varios métodos para asignar una matriz de tipo intrínseco y todos estos métodos son correctos, aunque cuál elegir, depende ...

Inicialización manual de todos los elementos en bucle.

int* p = new int[10];
for (int i = 0; i < 10; i++)
{
    p[i] = 0;
}

Usando la std::memsetfunción de<cstring>

int* p = new int[10];
std::memset(p, 0, sizeof(int) * 10);

Usando std::fill_nalgoritmo de<algorithm>

int* p = new int[10];
std::fill_n(p, 10, 0);

Utilizando std::vectorcontenedor

std::vector<int> v(10); // elements zero'ed

Si C ++ 0x está disponible, usando las características de la lista de inicializador

int a[] = { 1, 2, 3 }; // 3-element static size array
vector<int> v = { 1, 2, 3 }; // 3-element array but vector is resizeable in runtime
mloskot
fuente
1
debería ser vector <int> Si agregó p = new int [10] (), tenía una lista completa.
karsten
@mloskot, en el primer caso en el que ha inicializado una matriz usando "nuevo", ¿cómo pasará la referencia? Si usara int array[SIZE] ={1,2,3,4,5,6,7};notación, puedo usar void rotateArray(int (& input)[SIZE], unsigned int k);sería mi declaración de función, ¿qué sería al usar la primera convención? ¿cualquier sugerencia?
Anu
1
Me temo que el ejemplo con std::memsetes incorrecto: pasa 10, parece esperar un número de bytes; consulte en.cppreference.com/w/cpp/string/byte/memset . (Creo que esto muestra muy bien por qué debería uno evitar tal construcción de bajo nivel cuando sea posible.)
Suma
@Suma ¡Buena captura! Fijo. Esto parece ser un candidato para un error de una década :-) Sí, estoy de acuerdo con tu comentario.
mloskot
7

Si la memoria que está asignando es una clase con un constructor que hace algo útil, el operador new llamará a ese constructor y dejará su objeto inicializado.

Pero si está asignando un POD o algo que no tiene un constructor que inicialice el estado del objeto, entonces no puede asignar memoria e inicializar esa memoria con el operador nuevo en una operación. Sin embargo, tiene varias opciones:

1) Use una variable de pila en su lugar. Puede asignar e inicializar por defecto en un paso, de esta manera:

int vals[100] = {0};  // first element is a matter of style

2) uso memset(). Tenga en cuenta que si el objeto que está asignando no es un POD , es una mala idea memorizarlo. Un ejemplo específico es que si configura una clase que tiene funciones virtuales, eliminará la vtable y dejará su objeto en un estado inutilizable.

3) Muchos sistemas operativos tienen llamadas que hacen lo que usted desea: asignar en un montón e inicializar los datos a algo. Un ejemplo de Windows seríaVirtualAlloc()

4) Esta suele ser la mejor opción. Evite tener que manejar la memoria usted mismo. Puede usar contenedores STL para hacer casi cualquier cosa que haría con la memoria sin procesar, incluida la asignación e inicialización de una sola vez:

std::vector<int> myInts(100, 0);  // creates a vector of 100 ints, all set to zero
John Dibling
fuente
6

Sí hay:

std::vector<int> vec(SIZE, 0);

Use un vector en lugar de una matriz asignada dinámicamente. Los beneficios incluyen no tener que molestarse en eliminar explícitamente la matriz (se elimina cuando el vector se sale del alcance) y también que la memoria se elimina automáticamente incluso si se produce una excepción.

Editar: para evitar más votos negativos por parte de personas que no se molestan en leer los comentarios a continuación, debo dejar más claro que esta respuesta no dice que el vector sea siempre la respuesta correcta. Pero seguro que es una forma más C ++ que "manualmente" asegurándose de eliminar una matriz.

Ahora con C ++ 11, también hay std :: array que modela una matriz de tamaño constante (frente a un vector que puede crecer). También hay std :: unique_ptr que gestiona una matriz asignada dinámicamente (que se puede combinar con la inicialización como se responde en otras respuestas a esta pregunta). Cualquiera de esos es una forma más C ++ que manejar manualmente el puntero a la matriz, en mi humilde opinión.

villintehaspam
fuente
11
esto realmente no responde la pregunta que se hizo.
John Knoeller
1
¿Debo usar siempre en std::vectorlugar de arreglos asignados dinámicamente? ¿Cuáles son los beneficios de usar una matriz sobre un vector y viceversa?
dreamlax
1
@ John Knoller: El OP preguntó sobre una forma de C ++ para hacerlo, yo diría que el vector es la forma de C ++ para hacer esto. Por supuesto, tiene razón en que podría haber situaciones que aún requerirían una matriz simple y sin saber la situación del OP, esta podría ser una. Sin embargo, supongo que no, ya que parece plausible que el OP no sepa acerca de los vectores.
villintehaspam
1
@villintehaspam: aunque esta solución no responde a mi pregunta, es el camino que voy a tomar. Tyler McHenry responde mi pregunta más directamente, y debería ayudar especialmente a las personas que no pueden, por cualquier razón, usar std::vector.
dreamlax
2
@villintehaspam: No, no es una forma C ++ de hacerlo. Es una forma Java de hacerlo. Pegarse en vectortodas partes, independientemente del contexto, se llama "Escribir código Java en C ++".
AnT
2

std::filles unidireccional Toma dos iteradores y un valor para llenar la región. Eso, o el bucle for, sería (supongo) la forma más C ++.

Para establecer una matriz de tipos enteros primitivos en 0 específicamente, memsetestá bien, aunque puede levantar las cejas. Considere también calloc, aunque es un poco incómodo usarlo desde C ++ debido al reparto.

Por mi parte, casi siempre uso un bucle.

(No me gusta adivinar las intenciones de las personas, pero es cierto que std::vector, en igualdad de condiciones, es preferible usarlas new[]).


fuente
1

siempre puedes usar memset:

int myArray[10];
memset( myArray, 0, 10 * sizeof( int ));
Gregor Brandt
fuente
Entiendo que puedo usar memset, pero no estaba seguro de si esta era la forma en C ++ de abordar el problema.
dreamlax
1
En realidad, no es 'la forma C ++', pero tampoco lo son las matrices sin formato.
Pete Kirkham
1
@gbrandt: Lo que quiere decir que no funciona muy bien en C o C ++. Funciona para la mayoría de los valores del tipo es char o unsigned char. Funciona para la mayoría de los tipos del valor es 0 (al menos en la mayoría de las implementaciones). De lo contrario, generalmente es inútil.
Jerry Coffin
1
10 * sizeof( *myArray )está más documentado y a prueba de cambios que 10 * sizeof( int ).
Kevin Reid
1
En cualquier caso, el OP tiene una matriz sin procesar y memset es la forma más rápida y fácil de poner a cero esa matriz.
Gregor Brandt
-1

Por lo general, para listas dinámicas de elementos, utiliza a std::vector.

En general, uso memset o un bucle para la asignación dinámica de memoria sin procesar, dependiendo de qué tan variable prevea que será esa área de código en el futuro.

Paul Nathan
fuente