OVH Cloud OVH Cloud

STL set contenant des pointeurs

15 réponses
Avatar
Frédéric Mayot
Bonjour,

Voici mon problème :

J'ai une collection d'instances d'une classe A qui contient un membre
string appelé M.

Je veux conserver cette collection de manière triée, donc un set<A*> me
semblait être une bonne idée pourvu que je définisse un foncteur pour
comparer mes pointeurs.

Pour l'insertion et le parcours total, pas de problème. Maintenant, et
vous vous en douterez, si je veux connaître l'existence d'un nom dans
ma collection, je souhaiterais que cela se fasse sans parcourir l'arbre
intégralement (mais plutôt en profitant de la structure de l'arbre rouge
et noir). Mais là, je suis coincé...

Y a-t-il une autre solution que de dupliquer M en l'utilisant un
map<string, A*> ?

Merci.

Fred

5 réponses

1 2
Avatar
Loïc Joly
Bertrand Motuelle wrote:

Loïc Joly wrote in message news:<bnmsn2$c05$...

Frédéric Mayot wrote:

A* bidon = new A(....);
bidon->M = "Toto";
if (mySet.find(bidon) != mySet.end())
// Trouvé




Tu ne voulais pas plutôt dire:
A bidon;
bidon.M = "Toto";
if (mySet.find(&bidon) != mySet.end())
// Trouvé


Pourquoi pas, je ne voulais pas donner d'écriture spéciale, juste une
idée générale. Ton écriture évite effectivement le delete, mais en fait,
souvent quand j'ai un conteneur de pointeurs, c'est pour le
polymorphisme, et dans ce cas, A est souvent abstraite et à construire
depuis une factory. C'est pourquoi sans réfléchir, j'ai pris l'écriture
avec allocation dynamique.

--
Loïc


Avatar
Bertrand Motuelle
Frédéric Mayot wrote:
Bonjour,

Voici mon problème :

J'ai une collection d'instances d'une classe A qui contient un membre
string appelé M.

Je veux conserver cette collection de manière triée, donc un set<A*> me
semblait être une bonne idée pourvu que je définisse un foncteur pour
comparer mes pointeurs.

Pour l'insertion et le parcours total, pas de problème. Maintenant, et
vous vous en douterez, si je veux connaître l'existence d'un nom dans
ma collection, je souhaiterais que cela se fasse sans parcourir l'arbre
intégralement (mais plutôt en profitant de la structure de l'arbre rouge
et noir). Mais là, je suis coincé...

Y a-t-il une autre solution que de dupliquer M en l'utilisant un
map<string, A*> ?


Dans des cas comme celà, j'utilise en général comme clé d'une map une
petite classe comme celle-ci:

class StringKey
{
public:
StringKey(const std::string& key) : key_(key) {}

bool operator < (const StringKey& rhs) const
{
return key_ < rhs.key_;
}

private:
const std::string& key_;
};


Le type de la map est donc:
std::map<StringKey, A*> theMap;

L'ajout dans cette map:
A* a = new A;
A->M = "toto";
...
theMap[StringKey(a->M)] = a;

La recherche:
std::map<StringKey, A*>::const_iterator elt theMap.find(StringKey("tata"));


Ca a l'avantage de ne pas dupliquer d'informations.

Bertrand.

Avatar
amerio
Dans des cas comme celà, j'utilise en général comme clé d'une map une
petite classe comme celle-ci:

class StringKey
{
public:
StringKey(const std::string& key) : key_(key) {}

bool operator < (const StringKey& rhs) const
{
return key_ < rhs.key_;
}

private:
const std::string& key_;
};


Le type de la map est donc:
std::map<StringKey, A*> theMap;

L'ajout dans cette map:
A* a = new A;
A->M = "toto";
...
theMap[StringKey(a->M)] = a;

La recherche:
std::map<StringKey, A*>::const_iterator elt > theMap.find(StringKey("tata"));


Ca a l'avantage de ne pas dupliquer d'informations.


Mais c'est extrement déconseillé !!! Et dangereux !!!

1er exemple :
A* a = new A;
A->M = "toto";
theMap[StringKey(a->M)] = a;
a->M = "gros bug"; // Le map n'est plus trié, donc plus de recherche possible

2eme exemple :
A* a = new A;
A->M = "toto";
theMap[StringKey(a->M)] = a;
delete a; // Le map contient une reference détruite, gare aux plantage

A éviter absolument, donc.

Avatar
tib.motuelle
"amerio" wrote in message news:<WAAob.80468$...
Dans des cas comme celà, j'utilise en général comme clé d'une map une
petite classe comme celle-ci:

class StringKey
{
public:
StringKey(const std::string& key) : key_(key) {}
bool operator < (const StringKey& rhs) const
{
return key_ < rhs.key_;
}
private:
const std::string& key_;
};

Le type de la map est donc:
std::map<StringKey, A*> theMap;

L'ajout dans cette map:
A* a = new A;
A->M = "toto";
...
theMap[StringKey(a->M)] = a;

La recherche:
std::map<StringKey, A*>::const_iterator elt > > theMap.find(StringKey("tata"));
Ca a l'avantage de ne pas dupliquer d'informations.


Mais c'est extrement déconseillé !!!
Je ne pense pas.


Et dangereux !!!
Pas si on sait ce que l'on fait.



1er exemple :
A* a = new A;
A->M = "toto";
theMap[StringKey(a->M)] = a;
a->M = "gros bug"; // Le map n'est plus trié, donc plus de recherche possible


Un code réel utiliserait l'encapsulation pour éviter de tels
désagréments.

2eme exemple :
A* a = new A;
A->M = "toto";
theMap[StringKey(a->M)] = a;
delete a; // Le map contient une reference détruite, gare aux plantage


Certes. Donc suivant ta logique std::list<A*>, std::vector<A*>... sont
"extrêmement déconseillés et dangereux".
Encore une fois, pas si on sait ce que l'on fait.

A éviter absolument, donc.
Pas convaincu.


Bertrand.


Avatar
amerio
La recherche:
std::map<StringKey, A*>::const_iterator elt >>> theMap.find(StringKey("tata"));
Ca a l'avantage de ne pas dupliquer d'informations.


Mais c'est extrement déconseillé !!!
Je ne pense pas.

Et dangereux !!!
Pas si on sait ce que l'on fait.

Même. C'est justement là que les pb arrive. On ne les vois plus arriver.

Il y a toujours un endroit ou on va "oublier" qu'il ne fallait pas modifier a->M.
(ou bien un utilisateur de A ne saura pas qu'il ne doit pas, etc...)
Mais si a est trés encapsulé, les risques seront moindres.
Mon but était juste de mettre le doigt sur un pb, qui ne saute pas aux yeux avec la
syntaxe choisie.

1er exemple :
A* a = new A;
A->M = "toto";
theMap[StringKey(a->M)] = a;
a->M = "gros bug"; // Le map n'est plus trié, donc plus de recherche
possible


Un code réel utiliserait l'encapsulation pour éviter de tels
désagréments.


Oui. Mais il y a un risque qd même. Avec M const, il y en aurait déjà moins.

2eme exemple :
A* a = new A;
A->M = "toto";
theMap[StringKey(a->M)] = a;
delete a; // Le map contient une reference détruite, gare aux plantage


Certes. Donc suivant ta logique std::list<A*>, std::vector<A*>... sont
"extrêmement déconseillés et dangereux".
Encore une fois, pas si on sait ce que l'on fait.


Non, car dans std::list<A*>, la syntaxe même montre qu'on stocke des pointeurs.
Et on peut deleté un élement, puis le supprimer de la liste.
Avec sa map, on ne peut plus. Il faut d'abord le supprimer, puis le délété.
Et si il a pris de parti de supprimer a du map dans le destructeur de A ?
Bref, il y a des risques cachés !

A éviter absolument, donc.
Pas convaincu.

Si tu fais attention à tout, c'est vrai qu'il n'y a de risques nulle part ;-)

Même dans les void* ;-)



1 2