197 votes

Comment initialiser la mémoire avec l'opérateur new en C++ ?

Je commence tout juste à m'intéresser au C++ et je souhaite prendre quelques bonnes habitudes. Si je viens d'allouer un tableau de type int avec le new opérateur, comment puis-je les initialiser tous à 0 sans les passer en boucle moi-même ? Devrais-je simplement utiliser memset ? Existe-t-il une méthode "C++" pour le faire ?

20 votes

Si vous voulez prendre de bonnes habitudes en C++, évitez d'utiliser les tableaux directement et utilisez plutôt les vecteurs. Le vecteur initialisera tous les éléments, quel que soit leur type, et vous n'aurez pas besoin de vous souvenir d'appeler l'opérateur delete[].

0 votes

@brianegge : Et si je dois passer un tableau à une fonction C externe, puis-je simplement lui donner le vecteur ?

12 votes

Vous pouvez passer &vector[0] .

450voto

Pavel Minaev Points 60647

C'est une caractéristique étonnamment peu connue du C++ (comme le prouve le fait que personne n'a encore donné cette réponse), mais il dispose d'une syntaxe spéciale pour initialiser un tableau par une valeur :

new int[10]();

Notez que vous debe utiliser 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 des valeurs).

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

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

...

  • Si le nouvel initialisateur est de la forme () l'élément est initialisé en valeur (8.5) ;

et ne limite pas les types pour lesquels cela est autorisé, alors que la méthode (expression-list) 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.

2 votes

Bien que je sois d'accord pour dire que c'est peu connu, je ne peux pas (entièrement) être d'accord pour dire que c'est vraiment très surprenant -- cela a été ajouté dans C++ 03, que la plupart des gens semblent avoir presque ignoré (puisque c'était l'une des rares choses nouvelles qu'il a ajoutées).

2 votes

@Jerry : Je dois admettre que je ne le savais pas encore (probablement parce que lorsque j'ai commencé à lire la norme, il s'agissait déjà de C++03). Cela dit, il est remarquable que toutes les implémentations que je connais supportent cela (je suppose que c'est parce que c'est si trivial à implémenter).

3 votes

Oui, c'est assez simple à mettre en œuvre. Pour ce qui est de la nouveauté, tous L'"initialisation de la valeur" est une nouveauté de C++ 03.

27voto

mloskot Points 13971

Il existe un certain nombre de 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 de la boucle

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

Utilisation std::memset de la fonction <cstring>

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

Utilisation std::fill_n algorithme de <algorithm>

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

Utilisation std::vector conteneur

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

Si C++11 est disponible, en utilisant liste des initiateurs caractéristiques

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

1 votes

Devrait être vector<int> Si vous avez ajouté p= new int[10](), vous avez obtenu une liste complète.

0 votes

@mloskot, dans le premier cas où vous avez initialisé un tableau en utilisant "new", comment se passe le passage par référence ? Si j'utilise int array[SIZE] ={1,2,3,4,5,6,7}; je peux utiliser la notation void rotateArray(int (& input)[SIZE], unsigned int k); serait ma déclaration de fonction, qu'en serait-il si j'utilisais la première convention ? une suggestion ?

2 votes

Je crains que l'exemple avec std::memset est erroné - vous passez 10, il semble s'attendre à un nombre d'octets - voir fr.cppreference.com/w/cpp/string/byte/memset . (Je pense que cela montre bien pourquoi il faut éviter, dans la mesure du possible, ce type de construction de bas niveau).

26voto

Tyler McHenry Points 35551

En supposant que vous vouliez vraiment un tableau et non un std::vector, la "méthode 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 attention, sous le capot, il s'agit toujours d'une boucle qui affecte chaque élément à 0 (il n'y a pas vraiment d'autre moyen de le faire, à moins d'une architecture spéciale avec un support matériel).

0 votes

Je ne vois pas d'inconvénient à ce que la boucle soit implémentée sous une fonction, je voulais simplement savoir si je devais ou non implémenter moi-même une telle boucle. Merci pour le conseil.

4 votes

Vous pourriez être surpris. Je l'ai été. Sur mon STL (à la fois GCC et Dinkumware), std::copy se transforme en memcpy s'il détecte qu'il est appelé avec des types intégrés. Je ne serais pas surpris que std::fill_n utilise memset.

2 votes

Non. Utilisez 'Value-Initialization' pour mettre tous les membres à 0.

8voto

John Dibling Points 56814

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 n'a pas de constructeur qui initialise l'état de l'objet, alors vous ne pouvez pas allouer de la mémoire et initialiser cette mémoire avec l'opérateur new en une seule opération. Cependant, plusieurs options s'offrent à vous :

  1. Utilisez plutôt une variable de pile. Vous pouvez allouer et initialisation par défaut en une seule étape, comme ceci :

     int vals[100] = {0}; // first element is a matter of style
  2. utiliser memset() . Notez que si l'objet que vous allouez n'est pas un POD, le memsetting est une mauvaise idée. Par exemple, si vous mémorisez une classe qui possède des fonctions virtuelles, vous ferez disparaître la table virtuelle et laisserez votre objet dans un état inutilisable.

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

  4. C'est généralement la meilleure option. Vous éviterez ainsi de devoir 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 en une seule fois :

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

7voto

villintehaspam Points 4470

Oui, c'est le cas :

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

Utiliser un vecteur au lieu d'un tableau alloué dynamiquement. L'avantage est qu'il n'est pas nécessaire de supprimer explicitement le tableau (il est supprimé lorsque le vecteur sort de son champ d'application) et que la mémoire est automatiquement supprimée même en cas d'exception.

Edit : Pour éviter de nouveaux downvotes de la part de personnes qui ne prennent pas la peine de lire les commentaires ci-dessous, je dois préciser que cette réponse ne dit pas que le vecteur est toujours la bonne réponse. Mais c'est une méthode plus C++ que de s'assurer "manuellement" de la suppression d'un tableau.

Maintenant, avec C++11, il y a aussi std::array qui modélise un tableau de taille constante (par opposition à un vecteur qui peut croître). Il y a aussi std::unique_ptr qui gère un tableau alloué dynamiquement (qui peut être combiné avec l'initialisation comme indiqué dans d'autres réponses à cette question). Toutes ces méthodes sont plus C++ que la gestion manuelle du pointeur sur le tableau, IMHO.

11 votes

Cela ne répond pas vraiment à la question posée.

1 votes

Dois-je toujours utiliser std::vector au lieu de tableaux alloués dynamiquement ? Quels sont les avantages d'utiliser un tableau plutôt qu'un vecteur, et vice versa ?

0 votes

J'aurais dû mentionner dans ma question que le tableau doit persister après le champ d'application actuel. Dois-je utiliser new std::vector ?

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X