Pointeurs vers des éléments de std::vector et std::list
J'ai un std::vector
avec des éléments d'une classe ClassA
. De plus, je veux créer un index en utilisant un std::map<key,ClassA*>
qui mappe une valeur de clé à des pointeurs vers des éléments contenus dans le vecteur.
Y a-t-il une garantie que ces pointeurs restent valides (et pointent vers le même objet) lorsque des éléments sont ajoutés à la fin du vecteur (pas inséré). I. e, le code suivant correcte:
std::vector<ClassA> storage;
std::map<int, ClassA*> map;
for (int i=0; i<10000; ++i) {
storage.push_back(ClassA());
map.insert(std::make_pair(storage.back().getKey(), &(storage.back()));
}
// map contains only valid pointers to the 'correct' elements of storage
Quelle est la situation, si j'utilise std::list
au lieu de std::vector
?
7 réponses
Vecteurs-Non. Comme la capacité des vecteurs ne se rétrécit jamais, il est garanti que les références, les pointeurs et les itérateurs restent valides même lorsque les éléments sont supprimés ou modifiés, à condition qu'ils se réfèrent à une position avant les éléments manipulés. Toutefois, les insertions peuvent invalider les références, les pointeurs et les itérateurs.
Listes-Oui, l'insertion et la suppression d'éléments n'invalide pas les pointeurs, les références et les itérateurs vers d'autres éléments
Pour autant que je sache, il n'y a pas une telle garantie. L'ajout d'éléments au vecteur entraînera une réallocation des éléments, invalidant ainsi tous vos pointeurs dans la carte.
Utiliser std::deque
! Les pointeurs vers les éléments sont stables lorsque seul push_back()
est utilisé.
Note: les itérateurs d'éléments peuvent être invalidés! Les pointeurs vers les éléments ne le seront pas.
Edit: cette réponse explique les détails pourquoi: l'itérateur de C++ deque invalidé après push_front()
Je ne suis pas sûr que ce soit garanti, mais en pratique storage.reserve(needed_size)
devrait s'assurer qu'aucune réallocation ne se produit.
Mais pourquoi ne stockez-vous pas les index?
Il est facile de convertir des index en itérateurs en les ajoutant à l'itérateur begin (storage.begin()+idx
) et il est facile de transformer n'importe quel itérateur en pointeur en le déréférençant d'abord, puis en prenant son adresse (&*(storage.begin()+idx)
).
Faites-leur simplement les deux pointeurs de magasin et supprimez explicitement les objets lorsque vous n'en avez pas besoin.
std::vector<ClassA*> storage;
std::map<int, ClassA*> map;
for (int i=0; i<10000; ++i) {
ClassA* a = new ClassA()
storage.push_back(a)
map.insert(std::make_pair(a->getKey(), a))
}
// map contains only valid pointers to the 'correct' elements of storage
D'un des commentaires à une autre réponse, il semble que tout ce que vous voulez est de centraliser (faciliter) la gestion de la mémoire. Si c'est vraiment le cas, vous devriez envisager d'utiliser des solutions préemballées comme la bibliothèque Boost pointer container et garder votre propre code aussi simple que possible.
En particulier, jetez un oeil à ptr_map
- pour les vecteurs no.
-
Pour les listes Oui. comment? itérateur fonctionne comme un pointeur sur un nœud particulier dans la liste. vous pouvez donc attribuer des valeurs à n'importe quelle structure comme:
Liste maliste;
Paire de temp;
Temp = make_pair( maliste.begin (), x);