for (std::vector<Objet*>::iterator it = v.begin() ; it != v.end() ; ++it)
{
// On supprime tous les éléments '123' du vecteur
if ((*it)->valeur == 123)
{
Objet* const obj = *it;
v.erase(it);
// Appel d'une fonction pour notifier la suppression de l'élément
f(obj);
delete obj;
// PROBLEME : 'it' est-il toujours valide ici ??? Peut-on
// continuer à itérer ?
}
}
Et si non, comment faire ?
Merci d'avance pour vos réponses.
Vincent
--
SL> Au fait elle est mieux ma signature maintenant ?
Oui. T'enlève encore les conneries que t'as écrit dedans et c'est bon.
-+- JB in <http://www.le-gnu.net> : Le neuneuttoyage par le vide -+-
Cette action est irreversible, confirmez la suppression du commentaire ?
Signaler le commentaire
Veuillez sélectionner un problème
Nudité
Violence
Harcèlement
Fraude
Vente illégale
Discours haineux
Terrorisme
Autre
Fabien LE LEZ
On Sun, 17 Aug 2003 00:57:48 +0200, Vincent Richard wrote:
for (std::vector<Objet*>::iterator it = v.begin() ; it != v.end() ; ++it) { // On supprime tous les éléments '123' du vecteur if ((*it)->valeur == 123) { Objet* const obj = *it;
J'aime pas le "const" ici. En effet, tu déclares ici un pointeur constant vers un "Objet". Or l'appel à "delete" va transformer ce pointeur en un pointeur invalide. Certes, le "const" est valide du point de vue C++, mais il ne me paraît pas logique.
v.erase(it);
// Appel d'une fonction pour notifier la suppression de l'élément f(obj); delete obj;
Perso, je mettrais "delete" avant "erase". Mais bon, ce n'est que mon feeling ;-)
// PROBLEME : 'it' est-il toujours valide ici ?
Non (du moins, ce n'est pas garanti).
Une solution est d'utiliser la valeur de retour de erase(), qui est sensée être un itérateur sur l'élément suivant :
for (std::vector<Objet*>::iterator it = v.begin(); it != v.end();) { // On supprime tous les éléments '123' du vecteur if ((*it)->valeur == 123) { Objet* const obj = *it; .. it= v.erase(it); } else { ++it; } }
Deux bémols toutefois : - il est des implémentations de la STL pour lesquelles erase() ne renvoie pas de valeur (celle de BC++ 5.02). Si tu n'utilises que des compilateurs récents, ça ne devrait pas poser de problèmes, mais garde ça à l'esprit en cas d'erreur de compilation lors d'un portage. - std::vector<> est adapté pour des ajouts/suppressions à la fin, et l'accès aléatoire. Il n'est pas du tout adapté à l'insertion/suppression d'éléments au milieu : non seulement c'est lent, mais en plus ça invalide tous les itérateurs. Si tu as beaucoup de suppressions à faire (et pas d'accès aléatoire), je conseille plutôt std::list<> :
for (std::list<Objet*>::iterator it = v.begin(); it != v.end();) { // On supprime tous les éléments '123' du vecteur if ((*it)->valeur == 123) { Objet* const obj = *it; .. v.erase(it++); } else { ++it; } }
-- Tout sur fr.* (FAQ, etc.) : http://www.usenet-fr.net/fur/ et http://www.aminautes.org/forums/serveurs/tablefr.html Archives : http://groups.google.com/advanced_group_search http://www.usenet-fr.net/fur/usenet/repondre-sur-usenet.html
On Sun, 17 Aug 2003 00:57:48 +0200, Vincent Richard
<chere-loque.MARRE-DE-LA-PUB@wanadoo.fr.invalid> wrote:
for (std::vector<Objet*>::iterator it = v.begin() ; it != v.end() ; ++it)
{
// On supprime tous les éléments '123' du vecteur
if ((*it)->valeur == 123)
{
Objet* const obj = *it;
J'aime pas le "const" ici. En effet, tu déclares ici un pointeur
constant vers un "Objet". Or l'appel à "delete" va transformer ce
pointeur en un pointeur invalide. Certes, le "const" est valide du
point de vue C++, mais il ne me paraît pas logique.
v.erase(it);
// Appel d'une fonction pour notifier la suppression de l'élément
f(obj);
delete obj;
Perso, je mettrais "delete" avant "erase". Mais bon, ce n'est que mon
feeling ;-)
// PROBLEME : 'it' est-il toujours valide ici ?
Non (du moins, ce n'est pas garanti).
Une solution est d'utiliser la valeur de retour de erase(), qui est
sensée être un itérateur sur l'élément suivant :
for (std::vector<Objet*>::iterator it = v.begin(); it != v.end();)
{
// On supprime tous les éléments '123' du vecteur
if ((*it)->valeur == 123)
{
Objet* const obj = *it;
..
it= v.erase(it);
}
else
{
++it;
}
}
Deux bémols toutefois :
- il est des implémentations de la STL pour lesquelles erase()
ne renvoie pas de valeur (celle de BC++ 5.02). Si tu n'utilises que
des compilateurs récents, ça ne devrait pas poser de problèmes, mais
garde ça à l'esprit en cas d'erreur de compilation lors d'un portage.
- std::vector<> est adapté pour des ajouts/suppressions à la
fin, et l'accès aléatoire. Il n'est pas du tout adapté à
l'insertion/suppression d'éléments au milieu : non seulement c'est
lent, mais en plus ça invalide tous les itérateurs. Si tu as beaucoup
de suppressions à faire (et pas d'accès aléatoire), je conseille
plutôt std::list<> :
for (std::list<Objet*>::iterator it = v.begin(); it != v.end();)
{
// On supprime tous les éléments '123' du vecteur
if ((*it)->valeur == 123)
{
Objet* const obj = *it;
..
v.erase(it++);
}
else
{
++it;
}
}
--
Tout sur fr.* (FAQ, etc.) : http://www.usenet-fr.net/fur/
et http://www.aminautes.org/forums/serveurs/tablefr.html
Archives : http://groups.google.com/advanced_group_search
http://www.usenet-fr.net/fur/usenet/repondre-sur-usenet.html
On Sun, 17 Aug 2003 00:57:48 +0200, Vincent Richard wrote:
for (std::vector<Objet*>::iterator it = v.begin() ; it != v.end() ; ++it) { // On supprime tous les éléments '123' du vecteur if ((*it)->valeur == 123) { Objet* const obj = *it;
J'aime pas le "const" ici. En effet, tu déclares ici un pointeur constant vers un "Objet". Or l'appel à "delete" va transformer ce pointeur en un pointeur invalide. Certes, le "const" est valide du point de vue C++, mais il ne me paraît pas logique.
v.erase(it);
// Appel d'une fonction pour notifier la suppression de l'élément f(obj); delete obj;
Perso, je mettrais "delete" avant "erase". Mais bon, ce n'est que mon feeling ;-)
// PROBLEME : 'it' est-il toujours valide ici ?
Non (du moins, ce n'est pas garanti).
Une solution est d'utiliser la valeur de retour de erase(), qui est sensée être un itérateur sur l'élément suivant :
for (std::vector<Objet*>::iterator it = v.begin(); it != v.end();) { // On supprime tous les éléments '123' du vecteur if ((*it)->valeur == 123) { Objet* const obj = *it; .. it= v.erase(it); } else { ++it; } }
Deux bémols toutefois : - il est des implémentations de la STL pour lesquelles erase() ne renvoie pas de valeur (celle de BC++ 5.02). Si tu n'utilises que des compilateurs récents, ça ne devrait pas poser de problèmes, mais garde ça à l'esprit en cas d'erreur de compilation lors d'un portage. - std::vector<> est adapté pour des ajouts/suppressions à la fin, et l'accès aléatoire. Il n'est pas du tout adapté à l'insertion/suppression d'éléments au milieu : non seulement c'est lent, mais en plus ça invalide tous les itérateurs. Si tu as beaucoup de suppressions à faire (et pas d'accès aléatoire), je conseille plutôt std::list<> :
for (std::list<Objet*>::iterator it = v.begin(); it != v.end();) { // On supprime tous les éléments '123' du vecteur if ((*it)->valeur == 123) { Objet* const obj = *it; .. v.erase(it++); } else { ++it; } }
-- Tout sur fr.* (FAQ, etc.) : http://www.usenet-fr.net/fur/ et http://www.aminautes.org/forums/serveurs/tablefr.html Archives : http://groups.google.com/advanced_group_search http://www.usenet-fr.net/fur/usenet/repondre-sur-usenet.html
Fabien LE LEZ
On Sun, 17 Aug 2003 09:25:49 +0200, Vincent Richard wrote:
v.erase(it++);
Je suppose qu'ici, il faut lire :
it = v.erase(it);
Non : ce que j'avais écrit fonctionne avec std::list<> ; de plus, il me semble que std::list<>::erase ne renvoie pas forcément un itérateur sur l'élément suivant.
-- Tout sur fr.* (FAQ, etc.) : http://www.usenet-fr.net/fur/ et http://www.aminautes.org/forums/serveurs/tablefr.html Archives : http://groups.google.com/advanced_group_search http://www.usenet-fr.net/fur/usenet/repondre-sur-usenet.html
On Sun, 17 Aug 2003 09:25:49 +0200, Vincent Richard
<chere-loque.MARRE-DE-LA-PUB@wanadoo.fr.invalid> wrote:
v.erase(it++);
Je suppose qu'ici, il faut lire :
it = v.erase(it);
Non : ce que j'avais écrit fonctionne avec std::list<> ; de plus, il
me semble que std::list<>::erase ne renvoie pas forcément un itérateur
sur l'élément suivant.
--
Tout sur fr.* (FAQ, etc.) : http://www.usenet-fr.net/fur/
et http://www.aminautes.org/forums/serveurs/tablefr.html
Archives : http://groups.google.com/advanced_group_search
http://www.usenet-fr.net/fur/usenet/repondre-sur-usenet.html
On Sun, 17 Aug 2003 09:25:49 +0200, Vincent Richard wrote:
v.erase(it++);
Je suppose qu'ici, il faut lire :
it = v.erase(it);
Non : ce que j'avais écrit fonctionne avec std::list<> ; de plus, il me semble que std::list<>::erase ne renvoie pas forcément un itérateur sur l'élément suivant.
-- Tout sur fr.* (FAQ, etc.) : http://www.usenet-fr.net/fur/ et http://www.aminautes.org/forums/serveurs/tablefr.html Archives : http://groups.google.com/advanced_group_search http://www.usenet-fr.net/fur/usenet/repondre-sur-usenet.html
Patrick Mézard
v.erase(it);
// Appel d'une fonction pour notifier la suppression de l'élément
f(obj); delete obj;
Perso, je mettrais "delete" avant "erase". Mais bon, ce n'est que mon feeling ;-)
Même si dans son exemple l'ordre n'a pas d'importance (à condition que le destructeur de Objet ne lance pas d'exception), tu préfères conserver (transitoirement) un élément invalide (car détruit) dans le conteneur avant de le retirer, plutôt que de retirer un élément valide avant de le détruire (quitte à risquer la fuite de mémoire) ?
Patrick Mézard
v.erase(it);
// Appel d'une fonction pour notifier la suppression de
l'élément
f(obj);
delete obj;
Perso, je mettrais "delete" avant "erase". Mais bon, ce n'est que mon
feeling ;-)
Même si dans son exemple l'ordre n'a pas d'importance (à condition que le
destructeur de Objet ne lance pas d'exception), tu préfères conserver
(transitoirement) un élément invalide (car détruit) dans le conteneur avant
de le retirer, plutôt que de retirer un élément valide avant de le détruire
(quitte à risquer la fuite de mémoire) ?
// Appel d'une fonction pour notifier la suppression de l'élément
f(obj); delete obj;
Perso, je mettrais "delete" avant "erase". Mais bon, ce n'est que mon feeling ;-)
Même si dans son exemple l'ordre n'a pas d'importance (à condition que le destructeur de Objet ne lance pas d'exception), tu préfères conserver (transitoirement) un élément invalide (car détruit) dans le conteneur avant de le retirer, plutôt que de retirer un élément valide avant de le détruire (quitte à risquer la fuite de mémoire) ?
Patrick Mézard
Patrick Mézard
Soit T un type quelconque
void g (T&);
vector<T> v= ...; vector<T>::iterator it= ...;
A priori, je préfère l'écriture
g (*it); v.erase (it);
à l'écriture
T temp (*it); v.erase (it); g (temp);
(quitte à risquer la fuite de mémoire) ?
Garder un pointeur sur lequel on a appelé delete peut éventuellement générer un core dump et/ou un comportement indéfini si on le déréférence, mais pas une fuite mémoire, puisque delete a été appelé.
Oui justement, en supposant que g() soit modifiante ET susceptible d'échouer en laissant T dans un état non-valide (et potentiellement en balançant une exception ou que sais-je), je préfère copier "it", le virer du conteneur, puis le modifier, pour ne pas me retrouver avec un élement dans un état indéterminé dans le conteneur.
Dans le cas soulevé par Vincent, tout cela est bien entendu surperflu car il manipule des pointeurs (donc constructeur de copie et assignation en throw(), donc erase() en throw()) et son destructeur ne lance bien sûr pas d'exceptions donc on est tranquilles.
Patrick Mézard
Soit T un type quelconque
void g (T&);
vector<T> v= ...;
vector<T>::iterator it= ...;
A priori, je préfère l'écriture
g (*it);
v.erase (it);
à l'écriture
T temp (*it);
v.erase (it);
g (temp);
(quitte à risquer la fuite de mémoire) ?
Garder un pointeur sur lequel on a appelé delete peut éventuellement
générer un core dump et/ou un comportement indéfini si on le
déréférence, mais pas une fuite mémoire, puisque delete a été appelé.
Oui justement, en supposant que g() soit modifiante ET susceptible d'échouer
en laissant T dans un état non-valide (et potentiellement en balançant une
exception ou que sais-je), je préfère copier "it", le virer du conteneur,
puis le modifier, pour ne pas me retrouver avec un élement dans un état
indéterminé dans le conteneur.
Dans le cas soulevé par Vincent, tout cela est bien entendu surperflu car il
manipule des pointeurs (donc constructeur de copie et assignation en
throw(), donc erase() en throw()) et son destructeur ne lance bien sûr pas
d'exceptions donc on est tranquilles.
Garder un pointeur sur lequel on a appelé delete peut éventuellement générer un core dump et/ou un comportement indéfini si on le déréférence, mais pas une fuite mémoire, puisque delete a été appelé.
Oui justement, en supposant que g() soit modifiante ET susceptible d'échouer en laissant T dans un état non-valide (et potentiellement en balançant une exception ou que sais-je), je préfère copier "it", le virer du conteneur, puis le modifier, pour ne pas me retrouver avec un élement dans un état indéterminé dans le conteneur.
Dans le cas soulevé par Vincent, tout cela est bien entendu surperflu car il manipule des pointeurs (donc constructeur de copie et assignation en throw(), donc erase() en throw()) et son destructeur ne lance bien sûr pas d'exceptions donc on est tranquilles.
Patrick Mézard
Fabien LE LEZ
On Sun, 17 Aug 2003 14:52:13 +0200, "Patrick Mézard" wrote:
Oui justement, en supposant que g() soit modifiante ET susceptible d'échouer en laissant T dans un état non-valide (et potentiellement en balançant une exception ou que sais-je), je préfère copier "it", le virer du conteneur, puis le modifier, pour ne pas me retrouver avec un élement dans un état indéterminé dans le conteneur.
Mais une fonction g() vraiment exception-safe doit : - soit nettoyer correctement l'objet et retourner normalement ; - soit laisser l'objet sans modification et lancer une exception.
Exemple :
void g (Objet* &o) { if (o.OnPeutSupprimer() == false) { throw "Supression impossible"; } /* Eventuellement ici, un code qui ne peut pas lancer d'exception */ delete o; }
Si tu as un objet dans un état indéfini, le code ne peut pas avoir un comportement défini de toutes façons : - soit tu l'enlèves du conteneur, avec un risque de fuite mémoire ; - soit tu le laisses dans le conteneur, avec un risque d'utiliser un objet invalide.
-- Tout sur fr.* (FAQ, etc.) : http://www.usenet-fr.net/fur/ et http://www.aminautes.org/forums/serveurs/tablefr.html Archives : http://groups.google.com/advanced_group_search http://www.usenet-fr.net/fur/usenet/repondre-sur-usenet.html
On Sun, 17 Aug 2003 14:52:13 +0200, "Patrick Mézard"
<patrick.mezard@ifrance.com> wrote:
Oui justement, en supposant que g() soit modifiante ET susceptible d'échouer
en laissant T dans un état non-valide (et potentiellement en balançant une
exception ou que sais-je), je préfère copier "it", le virer du conteneur,
puis le modifier, pour ne pas me retrouver avec un élement dans un état
indéterminé dans le conteneur.
Mais une fonction g() vraiment exception-safe doit :
- soit nettoyer correctement l'objet et retourner normalement ;
- soit laisser l'objet sans modification et lancer une
exception.
Exemple :
void g (Objet* &o)
{
if (o.OnPeutSupprimer() == false)
{
throw "Supression impossible";
}
/* Eventuellement ici, un code qui ne peut pas lancer d'exception */
delete o;
}
Si tu as un objet dans un état indéfini, le code ne peut pas avoir un
comportement défini de toutes façons :
- soit tu l'enlèves du conteneur, avec un risque de fuite
mémoire ;
- soit tu le laisses dans le conteneur, avec un risque
d'utiliser un objet invalide.
--
Tout sur fr.* (FAQ, etc.) : http://www.usenet-fr.net/fur/
et http://www.aminautes.org/forums/serveurs/tablefr.html
Archives : http://groups.google.com/advanced_group_search
http://www.usenet-fr.net/fur/usenet/repondre-sur-usenet.html
On Sun, 17 Aug 2003 14:52:13 +0200, "Patrick Mézard" wrote:
Oui justement, en supposant que g() soit modifiante ET susceptible d'échouer en laissant T dans un état non-valide (et potentiellement en balançant une exception ou que sais-je), je préfère copier "it", le virer du conteneur, puis le modifier, pour ne pas me retrouver avec un élement dans un état indéterminé dans le conteneur.
Mais une fonction g() vraiment exception-safe doit : - soit nettoyer correctement l'objet et retourner normalement ; - soit laisser l'objet sans modification et lancer une exception.
Exemple :
void g (Objet* &o) { if (o.OnPeutSupprimer() == false) { throw "Supression impossible"; } /* Eventuellement ici, un code qui ne peut pas lancer d'exception */ delete o; }
Si tu as un objet dans un état indéfini, le code ne peut pas avoir un comportement défini de toutes façons : - soit tu l'enlèves du conteneur, avec un risque de fuite mémoire ; - soit tu le laisses dans le conteneur, avec un risque d'utiliser un objet invalide.
-- Tout sur fr.* (FAQ, etc.) : http://www.usenet-fr.net/fur/ et http://www.aminautes.org/forums/serveurs/tablefr.html Archives : http://groups.google.com/advanced_group_search http://www.usenet-fr.net/fur/usenet/repondre-sur-usenet.html