OVH Cloud OVH Cloud

Comment bien désallouer un vector reservé ?

16 réponses
Avatar
Alex
Bonjour,
Dans mon programme, j'ai besoin d'avoir un espace contigue de string et pour
cela je fais :

std::vector<string> MesStrings;
MesString.reserve(1000); // <= Si je reserve pas, l'espace n'est
pas contigue et a chaque ajout il me change les adresses
// et dans mon prog il
me faut des adresses qui ne changent pas a chaque ajout

// J'ajoute kk string
for (int i=0;i<100;i++)
{
string temp = "toto";
MesString.push-back(temp);
}

Ensuite, pour désallouer l'espace je fais :
MesString.clear();

Mon compilateur (vc++) me dit que j'ai des fuites. J'ai noté que si je ne
reserve pas d'espace au debut, je n'ai pas ces memory-leak.

Quelqu'un peut-il m'expliquer le pourquoi du comment ???
Merci !

--
---------------------------------------------------------------------------
Envie de déco ? http://www.decoetalage.net

6 réponses

1 2
Avatar
Dimitri PAPADOPOULOS-ORFANOS
Bonjour,

Une petite question en passant... Si pendant l'execution du prog on se rend
compte que le reserve initial etait trop juste, y-a-til une maniere
d'allouer plus d'espace en gardant la mémoire contigue ? (enfin, disons en
"forcant" l'allocation a etre contigue; quitte a emmettre un message
d'erreur, ou ajuster e programme si cela n'a pas été possible)


Autant que je sache, la mémoire utilisée par vector est toujours
contiguë. Si ce n'est pas garanti par la norme actuelle, il me semble
que ça le sera dans la prochaine norme. En attendant, tous les
compilateurs que je connais (re-)allouent toujours un bloc de mémoire
contiguë pour vector.

Avatar
kanze
Fabien LE LEZ wrote:
On Mon, 7 Feb 2005 23:16:49 +0100, "Alex" :

MesString.reserve(1000);


- Il me semble que reserve() peut être une no-op (quelqu'un
peut confirmer ?)


Il est même garanti être un no-op si capacity() vaut plus que
son paramètre. Alors, dans une implémentation où le constructeur
initialise le vector avec max_size() capacité systèmatiquement.
(Mais évidemment, je parle des implémentations qu'on ne vera
jamais, parce qu'inutilisable.)

La norme garantit qu'après reserve, capacity() serait au moins
aussi grande que le paramètre de reserve(). Elle garantie aussi
que tant que la taille reste inférieur à cette capacité, les
pointeurs, les références et les itérateurs vers le vecteur
resteront valide. (En fait, ce qui importe, ce n'est pas ce que
fait reserve(), mais ce que ne peut pas faire les autres
fonctions par la suite.)

De toute façon, si un appel à reserve() change le
fonctionnement du programme (à part bien sûr la vitesse
d'exécution), tu as un bug quelque part.


Pas du tout. C'est une des b a ba des la durée de vie des
itérateurs. La raison d'être même de réserve, c'est de permettre
des push_back ou des insert par la suite sans invalider les
itérateurs.

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34


Avatar
kanze
Alex wrote:
It is guaranteed that no reallocation takes place during
insertions that happen after a call to reserve() until the
time when an insertion would make the size of the vector
greater than the size specified in the most recent call to
reserve().



Une petite question en passant... Si pendant l'execution du
prog on se rend compte que le reserve initial etait trop
juste, y-a-til une maniere d'allouer plus d'espace en gardant
la mémoire contigue ? (enfin, disons en "forcant" l'allocation
a etre contigue; quitte a emmettre un message d'erreur, ou
ajuster e programme si cela n'a pas été possible)


Un petit détail : la mémoire d'un std::vector est toujours
contigu. Ça fait partie des exigeances de vector.

Mais je crois en fait que ce dont tu as besoin, c'est que les
adresses des éléments restent valides. Et là, il n'y a rien. Si
tu n'as pas fait un reserve() assez grand, il n'y a rien à
faire.

Ceci dit, j'aimerais savoir un peu plus juste de quoi il s'agit.
Parce qu'il doit y avoir un moyen de contourner le problème.
Donc, par exemple, tu as parlé de renvoyer des void* chez des
clients. Qu'est-ce qu'ils font avec ces void* ? Est-ce que
renvoyer l'adresse du vector (plutôt que l'adresse de son
contenu) ne ferait pas l'affair, par exemple ?

--
James Kanze GABI Software
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



Avatar
Alex
Ceci dit, j'aimerais savoir un peu plus juste de quoi il s'agit.
Parce qu'il doit y avoir un moyen de contourner le problème.
Donc, par exemple, tu as parlé de renvoyer des void* chez des
clients. Qu'est-ce qu'ils font avec ces void* ? Est-ce que
renvoyer l'adresse du vector (plutôt que l'adresse de son
contenu) ne ferait pas l'affair, par exemple ?


Alors ca, c'est une très très bonne idée :) Merci ! (mais pourquoi n'y ai-je
pas pensé plus tot !!)

Avatar
Alex
Non, l'allocation supplémentaire n'a pas le droit d'être constante, il
faut qu'elle soit multiple de la taille courante. Sinon, on a une
complexitée amortie en O(n) pour l'insertion, au lieu de O(1).


En effet ça serait pas mal... je vais regarder dans les sources de stl si
c'est le cas.

Les fuites sont peut-être dues à "un bug" dans STL. C'est que reserve
1000 fait marcher 1000 fois le constructeur de string, qui fait new.


Reserve n'appelle pas de constructeurs. Tu confonds avec resize.


Oui, j'ai vu dans les sources sous Windows et que reserve() agit par
allocate() de la classe allocator qui alloue un espace contiguë en ayant la
SIZE passée à reserve() multiplié par la sizeof de l'objet paramètre du
template ! En effet, il n'y a pas de constructeur : ) Tout se passe ensuite
par des iterateurs _First, _Last et _End


Avatar
Alex
Alex wrote:

Merci pour ta reponse, mais en fait je viens de m'appercevoir que c'est un
autre vector dans mon prog qui etait mal désalloué... Je m'excuse pour le
"spam", j'aurais du chercher un peu plus avant de poster ici (j'avais cru
avoir cherché assez... mais non !)


Mais non, pas de quoi. Je soupçonnais une anomalie au niveau de STL mais
c'est toujours bien se justifier qu'il y en a pas. En tout cas ici.

En fait ce que l'on attend d'une classe, surtout templatisée et container
c'est que quelle que soit la méthode appelée de manière régulière, que la
gestion de mémoire se fasse correctement. Et dans ton bout de code tout
était dans la loi, alors il devait avoir une explication, et tu l'as trouvé
!

Par exemple dans le cas de MFC (aie, aie, désolé pour le HS !!!!) leur
classe CString admet un accès direct par GetBuffer() qui doit être suivie
par ReleaseBuffer(). Mais entre les 2 on peut écrire où on veut et le
résultat peut être inattendu.

Bien sur pour le string tu as troujour c_str(), et tu peux le passer à
const_cast et faire n'importe quoi, mais c'est pas très régulier tout ça...

1 2