web-dev-qa-db-fra.com

Comment initialiser la mémoire avec un nouvel opérateur en C ++?

Je commence tout juste à entrer dans le C++ et je veux acquérir de bonnes habitudes. Si je viens d'allouer un tableau de type int avec l'opérateur new, comment puis-je les initialiser tous à 0 sans les parcourir tous moi-même? Devrais-je simplement utiliser memset? Existe-t-il une manière de faire “C++”?

159
dreamlax

C'est une fonctionnalité étonnamment méconnue du C++ (comme en témoigne le fait que personne ne l'a déjà donnée comme réponse), mais elle a en fait une syntaxe spéciale pour initialiser un tableau:

new int[10]();

Notez que vous devez utilisez les parenthèses vides - vous ne pouvez pas, par exemple, utiliser (0) ou quoi que ce soit d'autre (c'est pourquoi cela n'est utile que pour l'initialisation de valeur).

Ceci est explicitement autorisé par ISO C++ 03 5.3.4 [expr.new]/15, qui dit:

Une nouvelle expression qui crée un objet de type T initialise cet objet comme suit:

...

  • Si le nouvel initialiseur est de la forme (), l'élément est initialisé par la valeur (8.5);

et ne limite pas les types pour lesquels cela est autorisé, alors que le (expression-list) forme est explicitement limitée par d'autres règles dans la même section, de sorte qu'elle n'autorise pas les types de tableaux.

355
Pavel Minaev

En supposant que vous vouliez vraiment un tableau et non un std :: vector, la "manière C++" serait la suivante:

#include <algorithm> 

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

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

Mais sachez que sous le capot, il ne s’agit en réalité que d’une boucle qui assigne chaque élément à 0 (il n’ya vraiment aucune autre façon de le faire, à l’exception d’une architecture spéciale avec une prise en charge de niveau matériel).

25
Tyler McHenry

Il existe de nombreuses méthodes pour allouer un tableau de type intrinsèque et toutes ces méthodes sont correctes, bien que celle à choisir dépende ...

Initialisation manuelle de tous les éléments en boucle

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

En utilisant std::memset fonction de <cstring>

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

En utilisant std::fill_n algorithme de <algorithm>

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

En utilisant std::vector conteneur

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

Si C++ 0x disponible, utilisez liste d'initialisation fonctionnalités

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
22
mloskot

Si la mémoire que vous allouez est une classe avec un constructeur qui fait quelque chose d’utile, l’opérateur new appellera ce constructeur et laissera votre objet initialisé.

Mais si vous allouez un POD ou quelque chose qui ne possède pas de constructeur initialisant l'état de l'objet, vous ne pouvez pas allouer de mémoire et initialiser cette mémoire avec nouvel opérateur en une seule opération. Cependant, vous avez plusieurs options:

1) Utilisez une variable de pile à la place. Vous pouvez allouer et default-initialize en une étape, comme ceci:

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

2) utilisez memset(). Notez que si l’objet que vous allouez n’est pas un POD , c’est une mauvaise idée. Un exemple spécifique est que si vous memset une classe qui a des fonctions virtuelles, vous supprimerez la vtable et laisserez votre objet dans un état inutilisable.

3) De nombreux systèmes d'exploitation ont des appels qui font ce que vous voulez - allouez sur un tas et initialisez les données à quelque chose. Un exemple de Windows serait VirtualAlloc()

4) Ceci est généralement la meilleure option. Évitez de gérer vous-même la mémoire. Vous pouvez utiliser les conteneurs STL pour faire à peu près tout ce que vous feriez avec de la mémoire brute, y compris l'allocation et l'initialisation de tout en un:

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

Oui il y a:

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

Utilisez un vecteur au lieu d'un tableau alloué dynamiquement. Les avantages comprennent le fait de ne pas avoir à s’efforcer de supprimer explicitement le tableau (celui-ci est supprimé lorsque le vecteur est hors de portée) et que la mémoire est automatiquement supprimée même s’il existe une exception.

Edit: pour éviter d’avoir encore plus de votes négatifs venant de gens qui ne se donnent pas la peine de lire les commentaires ci-dessous, je tiens à préciser que cette réponse ne dit pas que le vecteur est toujours la bonne réponse. Mais c’est certainement une méthode plus C++ que "manuellement" en veillant à supprimer un tableau.

Maintenant avec C++ 11, il existe également std :: array qui modélise un tableau de taille constante (vs vector capable de croître). Il existe également std :: unique_ptr qui gère un tableau alloué de manière dynamique (qui peut être combiné à une initialisation comme indiqué dans d'autres réponses à cette question). N'importe lequel de ceux-ci sont d'une manière plus C++ que la gestion manuelle du pointeur sur le tableau, à mon humble avis.

6
villintehaspam

std::fill est un moyen. Prend deux itérateurs et une valeur pour remplir la région. Cela, ou la boucle for, serait (je suppose) plus le moyen C++.

Pour définir spécifiquement un tableau de types entiers primitifs sur 0, memset convient, bien que cela puisse soulever des sourcils. Considérez également calloc, bien que l’utilisation de C++ soit un peu gênante à cause du cast.

Pour ma part, j'utilise presque toujours une boucle.

(Je n'aime pas deviner les intentions des gens, mais il est vrai que std::vector est, toutes choses égales par ailleurs, préférable à l'utilisation de new[].)

2
please delete me

vous pouvez toujours utiliser memset:

int myArray[10];
memset( myArray, 0, 10 * sizeof( int ));
1
Gregor Brandt