11
votes

Std :: vecteur change son adresse? Comment éviter

Les éléments vectoriels sont stockés contiguës, je suppose qu'il ne peut pas avoir la même adresse après que certains push_back's, car l'espace alloué initial ne pouvait pas suffire.

Je travaille sur un code où j'ai besoin d'une référence à un élément Dans un vecteur, comme: xxx

mais ce n'est pas nécessairement vrai que PTR contient une référence à V [0] , droit? Comment serait un bon moyen de le garantir?

Ma première idée serait d'utiliser un vecteur de pointeurs et d'une allocation dynamique. Je me demande s'il y a un moyen plus facile de le faire?

PS.: En fait, j'utilise un vecteur d'une classe au lieu d'INT, mais je pense que les problèmes sont les mêmes.


4 commentaires

Pourquoi ne pas obtenir & v [0] uniquement lorsque vous en avez besoin?


Vous pouvez conserver l'index de l'élément au lieu du pointeur, puis pour y accéder à () ou opérateur []


@KennyTM Bien que: il n'est pas expliqué ci-dessus, dans mon code actuel, je ne peux plus dire quel index pour accéder quand j'en ai besoin.


@DMitry Yudakov: bonne idée! Malheureusement, j'ai réalisé que l'utilisation de cette approche vous obligerait à faire beaucoup de changements, car j'ai déjà utilisé une structure avec des pointeurs dans d'autres parties du code, mais sans problèmes puisque je préalcule le vecteur.


8 Réponses :


9
votes

Vous pouvez augmenter la capacité de la matrice sous-jacente utilisée par le vecteur en appelant son Fonction de membre Membre: xxx

tant que vous ne mettez pas plus que 100 éléments dans le vecteur, pTR pointeront sur le premier élément.


4 commentaires

Étant donné que réserve se réserve au moins autant que vous demandez, vous pouvez mettre à la hauteur de la capacité Capacité dans le vecteur.


Le problème est maintenant que je ne sais pas combien d'espace sera utilisé. Est-ce une bonne idée d'utiliser une valeur suffisamment élevée? Dans certains cas, seuls 10 ^ 6 seraient sûrs, mais dans d'autres cas, 100 suffiraient.


Si vous ne savez pas combien d'éléments vous allez stocker, vous réservez une mauvaise idée! Vous ne faites que reporter votre bogue de pointeur pendant plus tard, lorsque le vecteur dépasse cette capacité!


Si vous ne connaissez pas à l'avance combien d'éléments vous allez insérer, utilisez un index dans le conteneur ou un pointeur intelligent comme Jerry Coffin suggère.



6
votes

Comment serait un bon moyen de le garantir?

std :: vecteur est garanti d'être toujours continu, mais la mise en œuvre est libre de réaffecter ou de stockage libre sur les opérations altérant le contenu du vecteur (vecteurs itérateurs, points-points ou références aux éléments deviennent indéniables. aussi).

Vous pouvez toutefois atteindre le résultat souhaité, cependant, en appelant réserve . IIRC, la norme garantit qu'aucune réaffectation n'est effectuée jusqu'à ce que la taille du vecteur soit supérieure à sa capacité réservée.

Généralement, je ferais attention avec cela (vous pouvez être rapidement piégé ...). Mieux vaut ne pas compter sur std :: vecteur <> :: réserve et itérateur persistance que si vous ne devez vraiment pas.


0 commentaires

2
votes

Comme James McNellis et Alexander Gessler ont déclaré, réserve est un bon moyen de pré-allouer de la mémoire. Cependant, pour l'exhaustivité, j'aimerais ajouter que les pointeurs doivent rester valables, toutes les opérations d'insertion / d'élimination doivent se produire de la queue du vecteur, sinon l'élément changeant invalidera à nouveau vos pointeurs.


2 commentaires

L'invalidation dépendra de l'utilisation des pointeurs. Si int * pTR = & V [0]; est destiné à représenter un élément spécifique de la liste, puis oui, il pourrait être invalidé, mais s'il est destiné à représenter le premier élément de la liste, Quoi qu'il en soit, il ne sera pas invalidé.


Vous avez raison, bien sûr. J'ai supposé que le cas d'utilisation était de faire référence à un élément spécifique de la liste, car l'inverse est beaucoup plus facile à traiter de l'utilisation des méthodes à votre disposition pour récupérer des éléments à des indices spécifiques.



1
votes

Selon vos besoins et votre cas d'utilisation, vous voudrez peut-être jeter un coup d'œil à Bibliothèque de conteneurs de pointeur de Boost .

Dans votre cas, vous pouvez utiliser boost :: pTR_Vector .


0 commentaires

5
votes

Une autre possibilité de possibilité serait un pointeur intelligent construit à des fins qui, au lieu de stocker une adresse stockerait l'adresse du vecteur lui-même avec l'index de l'article que vous aimez. Il ne poserait alors ceux-ci et obtenez l'adresse de l'élément uniquement lorsque vous la déréférez, quelque chose comme ceci: xxx

alors votre int * ptr = & v [0]; serait remplacé par quelque chose comme: vec_ptr pTR (v, 0);

Quelques points: Tout d'abord, si vous réorganisez les éléments de votre Vecteur entre le moment où vous créez le "pointeur" et le temps que vous avez la désarférence, il ne se référera plus à l'élément d'origine, mais à tout élément se produit à la position spécifiée. Deuxièmement, cela ne vérifie aucune plage, donc (par exemple) tenter d'utiliser l'élément 100 TH dans un vecteur qui ne contient que 50 éléments donnera un comportement non défini.


0 commentaires

4
votes

Si vous n'avez pas besoin de vos valeurs stockées contiguës, vous pouvez utiliser std :: deque au lieu de std :: vecteur. Il ne réaffecte pas, mais contient des éléments dans plusieurs morceaux de mémoire.


0 commentaires

17
votes

ne faites pas réserve pour reporter ce bug de pointeur suspendu - comme une personne qui a eu le même problème, haussa les épaules, réservé 1000, puis quelques mois plus tard ont passé des âges à essayer de comprendre un bug de mémoire étrange (La capacité de vecteur dépassée 1000), je peux vous dire que ce n'est pas une solution robuste.

Vous voulez Évitez de prendre l'adresse des éléments dans un vecteur si possible précisément parce que la nature imprévisible des réaffectations. Si vous devez, utiliser des itérateurs au lieu des adresses brutes, car les implémentations de stl cochées vous indiqueront quand elles sont devenues invalides, au lieu de s'écraser au hasard.

La meilleure solution consiste à changer votre conteneur:

  • Vous pouvez utiliser std :: Liste - Il n'invalide pas les itérateurs existants lors de l'ajout d'éléments, et seul l'itérateur à un élément effacé est invalidé lors de l'effacement
  • Si vous utilisez c ++ 0x, std :: vecteur > est une solution intéressante
  • Alternativement, l'utilisation de pointeurs et de nouveaux / suppribles n'est pas trop mauvais - juste N'oubliez pas de supprimer les pointeurs avant de les effacer . Il n'est pas difficile de faire de cette façon, mais il faut faire attention à ne pas causer une fuite de mémoire en oubliant une suppression. (Mark Ransom souligne également: il ne s'agit pas d'exception sûre, l'ensemble des contenus de vecteur est divulguée si une exception provoque la destruction du vecteur.)
  • Notez que le ptr_vector de Boost ne peut pas être utilisé en toute sécurité avec certains des algorithmes STL, ce qui peut être un problème pour vous.

2 commentaires

Pointeurs avec New / Supprimer Présentez un problème lorsqu'un vecteur est détruit lorsque vous n'attendez pas cela - à cause d'une exception lancée par exemple.


IMO: STD :: La liste a très peu d'usages. Je recommanderais STD :: Deque à la place.



1
votes

J'ai aussi rencontré ce problème et j'ai passé une journée entière juste pour réaliser l'adresse de vecteur modifiée et que les adresses enregistrées sont devenues invalides. Pour mon problème, ma solution était que

  1. Enregistrer les données brutes dans le vecteur et obtenir des indices relatives
  2. Une fois que le vecteur a cessé de croître, convertissez les indices en adresses de pointeur

    J'ai trouvé les travaux suivants

    1. Pointeurs [I] = indices [i] + (taille_t) et vecteur [0];
    2. Pointeurs [I] = & Vector [(((Taille_t) Indices [I]];

      Cependant, je n'ai pas compris comment utiliser vector.front () et je ne suis pas sûr de savoir si je devrais utiliser Pointants [I] = indices [i] * Tailleof (Vector) + (taille_t) et vecteur [0]. Je pense que la manière de référence (2) devrait être très sûre.


0 commentaires