Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Problème de pointeur avec vector après erase()

11 réponses
Avatar
none
Bonjour,

Voici un exemple très simplifié de mon problème:

std::vector<int> foo;

foo.push_back(1);
foo.push_back(2);
foo.push_back(3);

int *pun = &foo.at(0);
int *pdeux = &foo.at(1);
int *ptrois = &foo.at(2);

foo.erase(foo.begin() + 1);

Après erase(), *pdeux == 3.

Existe-t-il une solution pour qu'au momment ou erase() est invoqué,
pdeux pointe toujours vers 2 sachant que je ne peux pas réaffecter pun,
pdeux et ptrois explicitement ?

merci pour votre aide.

Xavier

10 réponses

1 2
Avatar
Fabien LE LEZ
On Tue, 27 Mar 2007 22:44:14 +0200, none <""none"@(none)">:

Existe-t-il une solution pour qu'au momment ou erase() est invoqué,
pdeux pointe toujours vers 2


Utiliser std::list<> à la place de std::vector<>.

Avatar
xavier
Fabien LE LEZ wrote:
On Tue, 27 Mar 2007 22:44:14 +0200, none <""none"@(none)">:

Existe-t-il une solution pour qu'au momment ou erase() est invoqué,
pdeux pointe toujours vers 2


Utiliser std::list<> à la place de std::vector<>.


le problème de std::list<> est que je perds l'indicage que j'utilise
beaucoup dans mon programme.
Remplacer std::vector<int> par std::vector<int*> semble fonctionner,
mais dans mon cas c'est pas très élégant.


Avatar
Fabien LE LEZ
On Tue, 27 Mar 2007 22:44:14 +0200, none <""none"@(none)">:

foo.erase(foo.begin() + 1);


D'une manière générale, quand tu modifies un vector<>, tu peux
t'attendre à ce que tous les pointeurs et itérateurs sur des éléments
soient invalidés.
(Ce n'est pas toujours le cas, mais ce me semble une mauvaise idée de
prendre le risque.)

Avatar
xavier
Fabien LE LEZ wrote:
On Tue, 27 Mar 2007 22:44:14 +0200, none <""none"@(none)">:

foo.erase(foo.begin() + 1);


D'une manière générale, quand tu modifies un vector<>, tu peux
t'attendre à ce que tous les pointeurs et itérateurs sur des éléments
soient invalidés.
(Ce n'est pas toujours le cas, mais ce me semble une mauvaise idée de
prendre le risque.)



si j'utilise std::vector<int*>, le pointeur est stocké directement dans
le container, il n'y a donc pas de risque de modification du pointeur, non ?


Avatar
Fabien LE LEZ
On Tue, 27 Mar 2007 23:11:14 +0200, xavier <xavier>:

le problème de std::list<> est que je perds l'indicage que j'utilise
beaucoup dans mon programme.


Tu perds un indiçage _efficace_.
On peut très bien avoir un indiçage sur std::list<> ; c'est juste plus
lent. Ton profiler te dira si la vitesse reste acceptable pour ton
application.

template <class T> std::list<T>::const_iterator
GetAt (std::list<T> const& tableau, size_t n)
{
std::list<T>::const_iterator reponse= tableau.begin();
for (size_t i=0; i<n; ++i)
{
if (reponse == tableau.end())
// Ici, éventuellement, lancer une exception
++reponse;
}
return reponse;
}

(et la même chose en non-const)


Remplacer std::vector<int> par std::vector<int*> semble fonctionner,
mais dans mon cas c'est pas très élégant.


Tu peux encapsuler tout ça dans une classe.

De toutes façons, si tu utilises beaucoup une structure donnée dans un
programme, il vaut AMHA mieux en faire une classe, que tu pourras
modifier au fur et à mesure de l'évolution de tes besoins.

Une bonne méthode est de commencer par écrire l'interface publique de
la classe, puis de t'occuper des membres privés (données et
implémentation).

Avatar
Fabien LE LEZ
On Tue, 27 Mar 2007 23:25:00 +0200, xavier <xavier>:

si j'utilise std::vector<int*>, le pointeur est stocké directement dans
le container, il n'y a donc pas de risque de modification du pointeur, non ?


Exact.

Avatar
xavier
Fabien LE LEZ wrote:
On Tue, 27 Mar 2007 23:11:14 +0200, xavier <xavier>:

le problème de std::list<> est que je perds l'indicage que j'utilise
beaucoup dans mon programme.


Tu perds un indiçage _efficace_.
On peut très bien avoir un indiçage sur std::list<> ; c'est juste plus
lent. Ton profiler te dira si la vitesse reste acceptable pour ton
application.

template <class T> std::list<T>::const_iterator
GetAt (std::list<T> const& tableau, size_t n)
{
std::list<T>::const_iterator reponse= tableau.begin();
for (size_t i=0; i<n; ++i)
{
if (reponse == tableau.end())
// Ici, éventuellement, lancer une exception
++reponse;
}
return reponse;
}

(et la même chose en non-const)


Remplacer std::vector<int> par std::vector<int*> semble fonctionner,
mais dans mon cas c'est pas très élégant.


Tu peux encapsuler tout ça dans une classe.

De toutes façons, si tu utilises beaucoup une structure donnée dans un
programme, il vaut AMHA mieux en faire une classe, que tu pourras
modifier au fur et à mesure de l'évolution de tes besoins.

Une bonne méthode est de commencer par écrire l'interface publique de
la classe, puis de t'occuper des membres privés (données et
implémentation).




Comme j'ai pas mal de code impliqué je vais réflechir un peu avant de
m'orienter vers une solution, merci beaucoup pour ces infos.


Avatar
Fabien LE LEZ
On Tue, 27 Mar 2007 23:55:08 +0200, xavier <xavier>:

Comme j'ai pas mal de code impliqué je vais réflechir un peu avant de
m'orienter vers une solution


De toutes façons, manifestement, tu as du code qui utilise
vector<int>, et vector<int> ne convient plus, il faut en changer.

Donc, tu devrais remplacer tous les vector<int> par MonTableau<int>,
MonTableau<> étant une classe qui a les fonctions publiques dont tu as
besoin. Tu peux alors changer l'implémentation de MonTableau<> au fur
et à mesure, sans changer son interface, et donc sans casser de code.

Avatar
James Kanze
On Mar 27, 10:44 pm, none <""none"@(none)"> wrote:

Voici un exemple très simplifié de mon problème:

std::vector<int> foo;

foo.push_back(1);
foo.push_back(2);
foo.push_back(3);

int *pun = &foo.at(0);
int *pdeux = &foo.at(1);
int *ptrois = &foo.at(2);

foo.erase(foo.begin() + 1);

Après erase(), *pdeux == 3.


Après erase(), tout utilisation de pdeux ou de ptrois à un
comportement indéfini. Formellement, tout peut se passer : un
core dump, le reformattage du disque dur, ... (Dans la pratique,
je ne crois pas qu'une implémentation soit possible où *pdeux ne
vaut pas 3, vue les autres contraits sur std::vector.)

Existe-t-il une solution pour qu'au momment ou erase() est invoqué,
pdeux pointe toujours vers 2 sachant que je ne peux pas réaffecter pun,
pdeux et ptrois explicitement ?


Certainement pas, vue qu'après l'erase(), l'élément qui
contenait 2 n'existe plus du tout, nulle part.

En général, après erase() sur un vector, des pointeurs, des
références et des itérateurs à l'élément effacé et à tous l es
éléments suivants sont invalids. Si ça ne convient pas, ce n'est
pas std::vector qu'il te faut.

Dans toutes les collections, après erase(), des pointeurs, des
références et des itérateurs à l'élément effacé sont invalids.
C'est difficile à concevoir comment ça pourrait être autrement ;
comment maintenir un pointeur valid à quelque chose qui n'existe
plus ?

J'ai comme une petite doute que tu dois revoir ta conception,
parce que si ta conception exige des pointeurs à des objets
inexistant, il y a un problème de base.

--
James Kanze (GABI Software) mailto:
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
James Kanze
On Mar 27, 10:59 pm, Fabien LE LEZ wrote:
On Tue, 27 Mar 2007 22:44:14 +0200, none <""none"@(none)">:

Existe-t-il une solution pour qu'au momment ou erase() est invoqué,
pdeux pointe toujours vers 2


Utiliser std::list<> à la place de std::vector<>.


Même pas. pdeux pointait vers l'élément qu'il a effacé. C'est un
comportement indéfini de s'en servir après erase(), quelque soit
la collection. Dans le cas de std::list<>, il pointera prèsque
sûrement à de la mémoire libérée, ce qui n'est pas très bien non
plus.

--
James Kanze (GABI Software) mailto:
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


1 2