web-dev-qa-db-fra.com

"Droit" moyen de désallouer un objet std :: vector

La première solution est:

std::vector<int> *vec = new std::vector<int>;
assert(vec != NULL);
// ...
delete vec;

Une alternative est:

std::vector<int> v;
//...
vec.clear();
vec.swap(std::vector<int>(vec));

La deuxième solution est une astuce - quelle est la "bonne" façon de le faire?

Mettre à jour:

Je suis conscient que le destructeur sera appelé une fois qu'il sera sorti de la pile, j'étais curieux de connaître d'autres méthodes.

32
Jacob

Le moyen le plus simple et le plus fiable de désallouer un vecteur est de le déclarer sur la pile et de ne rien faire.

void Foo() {
  std::vector<int> v;
  ...
}

C++ garantit que le destructeur de v sera appelé lors de l'exécution de la méthode. Le destructeur de std::vector s'assurera que la mémoire allouée est libérée. Tant que le type T du vector<T> aura la sémantique de désallocation C++ appropriée, tout ira bien. 

34
JaredPar

Le moyen le plus simple de libérer tout le stockage dans un vecteur, sans détruire l'objet vectoriel lui-même, est

vec = std::vector<int>();

Votre deuxième variante aura le même effet, mais elle sautera à travers plus d’arceaux. L'astuce "copier et échanger" libère toute capacité supplémentaire du vecteur et peut être utile si elle contient des données que vous souhaitez conserver. S'il n'y a pas de données, il n'est pas nécessaire de copier ou de permuter.

15
Mike Seymour
std::vector<int> vi;
/*Push lots of stuff into the vector*/

// clean it up in C++03
// no need to clear() first
std::vector<int>().swap(vi);

// clean it up in C++0x
// not a one liner, but much more idiomatic
vi.clear();
vi.shrink_to_fit();
12
deft_code

Je suis d'accord avec Mike Seymour

const int big_size = 10000;
vector<double> v( big_size );
cout << "Before clearing, the capacity of the vector is "
  << v.capacity() << " and its size is " << v.size();
v.clear();
cout << "\nAfter clearing, the capacity of the vector is "
  << v.capacity() << " and its size is " << v.size();
vector<double>().swap( v );

cout << "\nAfter swapping, the capacity of the vector is "
  << v.capacity() << " and its size is " << v.size();

vector<double> v1( big_size );
v1 = vector<double>();
cout << "\n After vector<double>();, the capacity of the vector is "
  << v1.capacity() << " and its size is " << v1.size();
3
Sam

N'utilisez pas les fonctions d'allocation de mémoire sauf si vous en avez vraiment besoin. Si votre classe a besoin d'un vecteur, adressez-vous toujours directement au membre std :: vector. Pas besoin de faire de la mémoire allouée ici.

Dans les cas où vous avez besoin du vecteur dynamique, son affectation et sa suppression, comme dans votre premier exemple, sont correctes à 100%.

Dans le deuxième exemple, l'appel à std :: swap est strictement prononcé, car la méthode clear efface le vecteur, le rendant vide. Un problème possible est qu’il n’ya aucune garantie que le vecteur libère réellement la mémoire, ce qui la restitue au système d’exploitation (ou à l’exécution). Le vecteur peut conserver la mémoire allouée au cas où vous rempliriez le vecteur juste après l'avoir effacé. L'appel à std :: swap peut être une astuce pour «forcer» le vecteur à libérer ses données internes, mais rien ne garantit que cela se produira réellement.

1
Patrick

Mon hypothèse est que vous avez un vecteur qui contient temporairement une grande quantité de données. Une fois que le vecteur a été effacé, il utilisera encore toute cette mémoire. Vous souhaitez libérer cette mémoire une fois que vous avez terminé, mais la fonction/l'objet avec lequel vous travaillez n'est pas terminé.

Solutions par ordre décroissant de désirabilité:

  1. Retravaillez le code pour que le vecteur utilisant le code soit dans son propre bloc/fonction/objet afin qu'il soit détruit naturellement
  2. Utilisez l’astuce d’échange pour ne pas avoir à vous assurer que le vecteur est désalloué en toutes circonstances. Sa durée de vie sera liée à l'objet/à la fonction dans laquelle vous vous trouvez.
  3. nouveau/supprimer le vecteur. Cela libérera un peu plus de mémoire que la méthode précédente, mais il sera également plus difficile de s’assurer qu’aucune mémoire n’est perdue.

La seule différence technique entre permutation et suppression est que le vecteur de base lui-même n'est pas détruit. Il s’agit là d’un léger surcoût qui ne mérite pas d’être inquiété (tant que vous finissez par détruire le vecteur)

Le facteur le plus important est de faciliter l’écriture du code correct, et je pense que l’échange est préférable à la suppression, mais est pire que de déplacer le vecteur ailleurs.

1
Winston Ewert

Si vous laissez simplement le vecteur sortir du champ d'application, il se nettoiera de manière appropriée sans travail supplémentaire. Si le vecteur est une variable membre d'une classe et que vous voulez qu'il désalloue son contenu avant que son propriétaire ne soit détruit, appelez simplement vec.clear ().

Si vous voulez conserver le vecteur mais libérer la mémoire qui contient son contenu, alors vec.swap(std::vector<int>()); le fera. Il n'est pas nécessaire de copier-construire le temporaire dans l'original sauf si vec contient des éléments que vous souhaitez conserver et que vous souhaitez simplement réduire la mémoire allouée à un niveau proche de la taille actuelle ().

0
Alan

Dans le cas où le vecteur doit vraiment être sur le tas, n'oubliez pas:

std::auto_ptr<std::vector<int> > vec(new std::vector<int>);

particulièrement utile avec un code comme:

std::auto_ptr<std::vector<int> > vec(vectorFactoryFunction());
0
Graphics Noob

Cette comparaison n’est pas valide car les exemples traitent de différents types d’objets: durée dynamique et durée locale. Vous pouvez appeler le destructeur OR en utilisant l’astuce de swap (aussi appelée shrink_to_fit) avec l’un ou l’autre. La méthode right dépend de la nécessité ou non de conserver l'objet vectoriel.

Par exemple, vous devrez peut-être le conserver s’il existe des références ou des pointeurs qui doit rester valide, auquel cas la réduction est le seul moyen, quelle que soit la manière dont elle a été allouée.

0
Cubbi

Je ne suis pas sûr pourquoi votre deuxième exemple utilise un constructeur de copie pour le temporaire plutôt qu'un constructeur par défaut. Cela vous éviterait la ligne de code .clear().

Vous pouvez faire ce générique pour n'importe quel objet, même s'il ne s'agit pas d'un conteneur. Je suppose ici que std :: swap est spécialisé dans l'appel de vector :: swap.

template<typename T>
void ResetToDefault(T & value)
{
    std::swap(T(), value);
}

std::vector<int> vec;  
//...  
ResetToDefault(vec);
0
Mark Ransom

Supprimer libère la mémoire, la mémoire est alors libre pour le prochain objet, mais le vecteur est parti. 

La 2ème astuce libère tout excès de mémoire mais laisse le vecteur intact, mais vide.

0
Martin Beckett

Bien que les deux semblent fonctionner, je ne vois aucune raison de ne pas appeler simplement supprimer sur le pointeur. Le vecteur doit avoir un destructeur appelé qui gérera tout le reste. 

0
Ben313