OVH Cloud OVH Cloud

probleme de erase() pour std::vector

10 réponses
Avatar
laurent
salut,

alors voila, jai fais un algo pour supprimer des elements d'un std::vector :

varvector::iterator i = variablelist.begin();
while (1)
{
variable *v = *i;
if (v->bloc >= bloc) {
delete v;
variablelist.erase(i);
}
if (i == variablelist.end()) break;
}


ya mieux?

merci d'avance

10 réponses

Avatar
Jean-Marc Bourguet
"laurent" writes:

salut,

alors voila, jai fais un algo pour supprimer des elements d'un std::vector :

varvector::iterator i = variablelist.begin();
while (1)
{
variable *v = *i;
if (v->bloc >= bloc) {
delete v;
variablelist.erase(i);
}
if (i == variablelist.end()) break;
}


ya mieux?


Oui.

Ton iterateur devient invalide a la premiere iteration.

Mets la condition dans le while, c'est plus robuste dans le cas ou la
liste est vide.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

Avatar
Falk Tannhäuser
laurent wrote:

varvector::iterator i = variablelist.begin();
while (1)
{
variable *v = *i;
if (v->bloc >= bloc) {
delete v;
variablelist.erase(i);
}
if (i == variablelist.end()) break;
}


Les problèmes :
- erase(i) invalide l'itérateur i, donc tu n'as plus le droit
de te servir de sa valeur,
- quand tu ne supprimes pas l'élément pointé par i, i n'avance pas
=> boucle infinie,
- si le vecteur est vide, ça se passe mal car tu essayes toucher
à des éléments inexistants - mieux vaut placer le test à l'ent rée
de la boucle.

Voici ce que je propose (ma boule de cristal me dit que 'varvector'
est un typedef pour 'std::vector<variable*>', que 'variable' est
une struct contenant un champ 'bloc' et qu'on est sûr que les
pointeurs contenus dans 'variablelist' pointent tous sur des objets
valides) :

for(varvector::iterator it = variablelist.begin(); it != variablel ist.end(); )
{
variable* pv = *it;
if(pv->bloc >= bloc)
{
*it = 0; // Pour être sûr de ne jamais avoir de pointeur inv alide dans le vecteur
delete pv;
it = v.erase(it);
}else
++it;
}


Falk

Avatar
Fabien LE LEZ
On Wed, 17 Nov 2004 19:44:15 +0100, Falk Tannhäuser
:

it = v.erase(it);


S'assurer avant que vector<>::erase renvoie bien un itérateur. J'ai vu
des compilos qui ne suivent pas la norme sur ce point.

Pour ce genre de compilo foireux :

for (int i=0; i<variablelist.size();)
{
if (variablelist[i]->bloc >= bloc)
{
variablelist.erase (variablelist.begin()+i);
}
else
{
++i;
}
}

Tant qu'on garde std::vector<>, cette façon de procéder ne me paraît
pas une complète hérésie -- après tout, vector<> est optimisé pour
l'accès aléatoire.


--
;-)

Avatar
Falk Tannhäuser
Fabien LE LEZ wrote:

S'assurer avant que vector<>::erase renvoie bien un itérateur. J'ai vu
des compilos qui ne suivent pas la norme sur ce point.


Il existe aussi la solution basée sur std::remove_if() - mais malheureusement,
elle ne permet pas à ma connaissance de 'deleter' les objets pointés lorsqu'on
l'utilise pour enlever des pointeurs "nus" du vecteur. Si 'varvector' était un
'std::vector<variable>' ou un 'std::vector<boost::shared_ptr<variable> >',
cela ne poserait pas de problème, une fois défini le foncteur qui va bien :
__________________________________________________________________________
template<typename ClassT, typename MemT, template<typename> class CmpFunc>
class member_comparator_helper
{
MemT ClassT::* pmem;
MemT value;
public:
member_comparator_helper(MemT ClassT::* pm, MemT const& v) : pmem(pm), value(v) {}

bool operator()(ClassT const& t) const
{ return CmpFunc<MemT>()(t.*pmem, value); }

bool operator()(ClassT const* t) const
{ return operator()(*t); }

// Opérateur acceptant boost::shared_ptr si nécessaire
}; // class member_comparator_helper

template<template<typename> class CmpFunc, typename ClassT, typename MemT>
inline member_comparator_helper<ClassT, MemT, CmpFunc> member_comparator(MemT ClassT::* pm, MemT const& v)
{ return member_comparator_helper<ClassT, MemT, CmpFunc>(pm, v); }

// ...
// puis il suffirait d'appeler

variablelist.erase(std::remove_if(variablelist.begin(),
variablelist.end(),
member_comparator<std::greater_equal>(&variable::bloc, Bloc)),
variablelist.end());
___________________________________________________________________________

Falk

Avatar
Eric Fournier
"laurent" a écrit dans le message de
news:419b91bd$0$2345$
salut,

alors voila, jai fais un algo pour supprimer des elements d'un std::vector
:


varvector::iterator i = variablelist.begin();
while (1)
{
variable *v = *i;
if (v->bloc >= bloc) {
delete v;
variablelist.erase(i);
}
if (i == variablelist.end()) break;
}


ya mieux?



Parce que tout le monde aime la STL, je suggère:

class monPred // Note: Pas vraiment un prédicat, puisqu'il a des effets
secondaires
{
private:
variable m_bloc;
public:
monPred(monType Bloc) : m_bloc(Bloc) {}
bool operator()(variable *arg)
{
if(arg->bloc > m_bloc)
{
delete arg;
return true;
}
else
return false;
}
};

erase(remove_if(variablelist.begin(), variablelist.end(), monPred(bloc)),
variablelist.end());

Ce qui devrait régler tout problème d'itérateur et de limite de boucle.

-Eric

merci d'avance




Avatar
kanze
Falk Tannhäuser wrote in message
news:<419bce21$0$5179$...

Fabien LE LEZ wrote:

S'assurer avant que vector<>::erase renvoie bien un itérateur. J'ai vu
des compilos qui ne suivent pas la norme sur ce point.


Il existe aussi la solution basée sur std::remove_if() - mais
malheureusement, elle ne permet pas à ma connaissance de 'deleter' les
objets pointés lorsqu'on l'utilise pour enlever des pointeurs "nus" du
vecteur. Si 'varvector' était un 'std::vector<variable>' ou un
'std::vector<boost::shared_ptr<variable> >', cela ne poserait pas de
problème, une fois défini le foncteur qui va bien : k


En fait, je crois qu'on a ici un cas où la sémantique de remove_if est
exactement ce qu'il faut :

varvector::iterator pivot
= std::remove_if( varlist.begin(), varlist.end(), MonPred() ) ;
std::for_each( pivot, varlist.end(), Deleter() ) ;
varlist.erase( pivot, varlist.end() ) ;

avec le MonPred qu'il faut et :

struct Deleter
{
void operator()( variable*& p )
{
variable* tmp = NULL ;
std::swap( tmp, p ) ;
delete tmp ;
}
} ;

--
James Kanze GABI Software http://www.gabi-soft.fr
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
Fabien LE LEZ
On 18 Nov 2004 23:52:49 -0800, :

std::for_each( pivot, varlist.end(), Deleter() ) ;


Uh ?
Il me semble que le contenu entre "pivot" et "end()" n'est pas
garanti. A moins que ça ait changé entre-temps ?


--
;-)

Avatar
James Kanze
Fabien LE LEZ writes:

|> On 18 Nov 2004 23:52:49 -0800, :

|> > std::for_each( pivot, varlist.end(), Deleter() ) ;

|> Uh ?

|> Il me semble que le contenu entre "pivot" et "end()" n'est pas
|> garanti. A moins que ça ait changé entre-temps ?

Bien vu. Je crois que tu as raison. Ce qu'il faudrait faire, c'est
d'effectuer les delete d'abord, en mettant le pointeur à null, puis
faire le replace/erase.

--
James Kanze
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
Avatar
Fabien LE LEZ
On 21 Nov 2004 15:15:38 +0100, James Kanze :

Ce qu'il faudrait faire, c'est
d'effectuer les delete d'abord, en mettant le pointeur à null, puis
faire le

replace


"remove" ici, non ?

/erase.


Mais ça oblige à faire deux fois le test. Or il n'est pas forcément
faisable après le delete.



--
;-)

Avatar
kanze
Fabien LE LEZ wrote in message
news:...
On 21 Nov 2004 15:15:38 +0100, James Kanze :

Ce qu'il faudrait faire, c'est
d'effectuer les delete d'abord, en mettant le pointeur à null, puis
faire le

replace


"remove" ici, non ?


Oui.

/erase.


Mais ça oblige à faire deux fois le test. Or il n'est pas forcément
faisable après le delete.


Ça oblige à faire deux tests différents. Dans la première passe, le test
sur la condition, pour savoir s'il faut faire delete ou non, et la
deuxième passe, sur null.

--
James Kanze GABI Software http://www.gabi-soft.fr
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