OVH Cloud OVH Cloud

[STL] conteneurs associatifs.

8 réponses
Avatar
David MAREC
Bonjour à tous, je développe un petit projet en C++ et je bute sur
l'utilisation du conteneur associatif «std::map».

Voici un résumé de ma structure:

///////////////////////////////////////////////////////
/* Une classe à dériver, car la fonction membre
«Acheter» fournit les éléments soit d'un objet externe
défini par «Achalander» soit construits directement dans la classe
dérivée.

*/

class cEtalage
{
std::vector<std::string> xPanier;

public:
cEtalage();
virtual ~cFruits();
bool virtual Acheter(
unsigned int n,
const std::string& produit,
void **panier);

};

class cFruits:cEtalage
{
public:
cFruits();
virtual ~cFruits();
Achalander(ProducteurDeFruits *camion);
bool virtual Acheter(
unsigned int n,
const std::string& produit,
void **panier);
}

class cLegumes:cEtalage
{
public:
cLegumes();
virtual ~cLegumes();
Achalander(ProducteurDeLegumes *camion);
bool virtual Acheter(unsigned int n, const std::string& produit, void
**panier);

}


class cSouk{

/* .. */
std::map<std::string,cEtalage*> MonPanier;
/* ... */
}

suivi d'un

/*..*/
cFruits MesFruits;
MonPanier.insert(std::make_pair<"MESFRUITS",&MesFruits>);
/*..*/

////////////////////////////////////////////////////////////

La dernière ligne provoque sous VC++6 un message d'erreur du type :
«impossible d'attacher MesFruits car des fonctions membres existent mais
sont inaccessibles».
Je l'écris de mémoire, je n'ai plus le compilateur et le code sous la main.
Veuillez m'en excuser.
Comment devrais-je construire ce conteneur, afin qu'il me permette
d'utiliser des classes dérivées de cEtalage ?


Dans le même domaine, puis-je directement dériver le constructeur de la
classe cEtalage dans les classes dérivées ?
Ex : cFruits::cFruits(ProducteurDeFruits *camion);
cLegumes::cLegumes(ProducteurDeLegumes *camion);


De la même manière, si j'ai une classe de type

class cFournissseur{
class cFournissseur(const std::string Adresse);
class ~cFournissseur();
}

et que j'en range les objets dans un
std::map<std::string, cFournisseur> xCarnet

l'objet créé par un xCarnet.insert(std::make_pair(
"Toto",cFournisseur("square Georges Brassens")));
est détruit juste derrière après son insertion.
Comment créer un conteneur de ce type, dans un «for» par exemple, sans
faire appel à «new» et aux pointeurs?

Quelque chose a du m'échapper dans le principe de fonctionnement des
«map» , ces problèmes sont surement liés à la même erreur.

8 réponses

Avatar
Thierry Miceli
MonPanier.insert(std::make_pair<"MESFRUITS",&MesFruits>);


make_pair est une fonction, donc tu devrais écrire:

MonPanier.insert(std::make_pair("MESFRUITS",&MesFruits));

ou encore:

MonPanier["MESFRUITS"] = &MesFruits;

Avatar
David MAREC
D'après Thierry Miceli:

MonPanier.insert(std::make_pair<"MESFRUITS",&MesFruits>);
make_pair est une fonction, donc tu devrais écrire:


MonPanier.insert(std::make_pair("MESFRUITS",&MesFruits));


Oui, c'est une faute de doigts.

ou encore:

MonPanier["MESFRUITS"] = &MesFruits;


Ce n'est pas tout à fait la même chose, mais ça donne la
même erreur.


Avatar
Fabien LE LEZ
On Tue, 10 Aug 2004 20:59:52 +0200, David MAREC
:

class cEtalage
{
public:
virtual ~cFruits();


Erreur ici : le destructeur doit s'appeler "~cEtalage()".
Maintenant, as-tu besoin d'un destructeur ? Il doit bien sûr exister
pour pouvoir être virtuel, mais généralement il n'a pas besoin de
contenir d'instructions :

class cEtalage
{
public:
virtual ~cEtalage() {}


/*..*/
cFruits MesFruits;
MonPanier.insert(std::make_pair<"MESFRUITS",&MesFruits>);
/*..*/


J'imagine que tu t'es assuré que la durée de vie de "MesFruits" est au
moins égale à celle de MonPanier ?

et que j'en range les objets dans un
std::map<std::string, cFournisseur> xCarnet

l'objet créé par un xCarnet.insert(std::make_pair(
"Toto",cFournisseur("square Georges Brassens")));
est détruit juste derrière après son insertion.


L'objet de classe "cFournisseur" est effectivement temporaire, mais
tout va bien, car map<>::insertt() en fait une copie.
Ça ne serait bien sûr pas le cas si tu écrivais
std::map<std::string, cFournisseur*>



--
;-)

Avatar
David MAREC
Bonjour,

Fabien LE LEZ :

class cEtalage
{
public:
virtual ~cFruits();



Erreur ici : le destructeur doit s'appeler "~cEtalage()".


Grmbl, encore une faute de doigt que j'étais persuadé d'avoir corrigée
avant l'expédition de l'article.

Maintenant, as-tu besoin d'un destructeur ? Il doit bien sûr exister
pour pouvoir être virtuel, mais généralement il n'a pas besoin de
contenir d'instructions :


Pour l'une des classes dérivée, oui. Elle devra effectuer des opérations
de <fermeture> ou du moins les vérifier.

[...]
cFruits MesFruits;
MonPanier.insert(std::make_pair<"MESFRUITS",&MesFruits>);
/*..*/



J'imagine que tu t'es assuré que la durée de vie de "MesFruits" est au
moins égale à celle de MonPanier ?


Oui et le problème n'apparait plus ce matin !
Tout ceci compile désormais, une facétie de VC++ sans doute.
Ca m'a juste fait perdre une demi-journée cette histoire.

et que j'en range les objets dans un
std::map<std::string, cFournisseur> xCarnet

l'objet créé par un xCarnet.insert(std::make_pair(
"Toto",cFournisseur("square Georges Brassens")));
est détruit juste derrière après son insertion.



L'objet de classe "cFournisseur" est effectivement temporaire, mais
tout va bien, car map<>::insertt() en fait une copie.
Ça ne serait bien sûr pas le cas si tu écrivais
std::map<std::string, cFournisseur*>


C'est bien ce que je pensais, ce mécanisme de copie n'est pas du tout
adapté à la classe que je stocke.Cela provoque des anomalies dans le
fonctionnement du système.

Merci de vos réponses.


Avatar
Michel Michaud
Dans news:4119e4b6$0$22029$, David

[...]
Oui, c'est une faute de doigts.
[...]
Grmbl, encore une faute de doigt que j'étais persuadé d'avoir
[...]

Oui et le problème n'apparait plus ce matin !
Tout ceci compile désormais, une facétie de VC++ sans doute.


Très très peu probable. Tu dois avoir fait une faute de doigt
encore une fois... Il ne faut jamais blâmé le compilateur
avant d'avoir vérifié, revérifié, rerevérifié, rererevérifié
ton code¹. Sinon tu auras toujours beaucoup plus de difficultés
lors du déboguage de tes programmes, car tu penseras trop
rapidement que ce n'est pas ta faute. Il y a des bogues dans
les compilateurs moyens, mais même le programmeur le plus
expérimenté en fait beaucoup plus régulièrement !

Ca m'a juste fait perdre une demi-journée cette histoire.


Alors prends quelques minutes pour trouver ce que tu as
vraiment fait... Ce sera du temps bien investi.

¹ Et encore, ce sera bon de vérifier encore quelques fois :-)

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/

Avatar
David MAREC
Bonsoir,
D'après Michel Michaud:

[...]
Oui, c'est une faute de doigts.
[...]
Grmbl, encore une faute de doigt que j'étais persuadé d'avoir
[...]

Oui et le problème n'apparait plus ce matin !
Tout ceci compile désormais, une facétie de VC++ sans doute.


Très très peu probable. Tu dois avoir fait une faute de doigt
encore une fois...


Les fautes de doigts précédentes sont dues à un autre logiciel facétieux, le
tout nouveau Xorg:
<news://411a5e08$0$29674$
Il n'est pas facile d'écrire quoi que ce soit dans ces conditions. :-)


Par contre, le code était identique, du moins _je_ n'ai pas fait la moindre
modification entre la veille et le matin.

Il ne faut jamais blâmé le compilateur
avant d'avoir vérifié, revérifié, rerevérifié, rererevérifié
ton code¹.


Bah, une demi-journée pour re-re-re-vérifier cette structure quasiment vide
(nettoyée de la majorité des lignes de code pour identifier l'erreur), je
trouve que c'est beaucoup, sans parler de la soirée passée à relire le
livre du maitre.¹

Je devrais surtout rebooter entre chaque plantage de VC++.
(Le processus de compilation se bloque de temps à autres.)

Il y a des bogues dans les compilateurs moyens,
mais même le programmeur le plus expérimenté
en fait beaucoup plus régulièrement !


Certes, mais dans le cas présent, j'ai perdu du temps parce que je n'ai
_pas_ mis en doute le compilateur.

J'aurais du l'essayer avec gcc. J'y penserais dès que je saurais m'en
servir. :-)



¹ Je vous laisse imaginer ma tête lorsque, ayant mandé la compilation par
pur réflexe après l'ouverture du projet, j'ai lu qu'elle s'était terminée
sans erreur.


Avatar
David MAREC
Bonsoir.
D'après Fabien LE LEZ:

et que j'en range les objets dans un
std::map<std::string, cFournisseur> xCarnet

l'objet créé par un xCarnet.insert(std::make_pair(
"Toto",cFournisseur("square Georges Brassens")));
est détruit juste derrière après son insertion.


L'objet de classe "cFournisseur" est effectivement temporaire, mais
tout va bien, car map<>::insertt() en fait une copie.


Si je comprend bien, l'objet temporaire va bel et bien être détruit par tous
les mécanismes idoines (~objet()) et la copie sera aussi créée en
appliquant les mécanismes associés (objet()) ?

Ça ne serait bien sûr pas le cas si tu écrivais
std::map<std::string, cFournisseur*>


Pour être plus précis, l'objet qui est utilisé dans le
«std::map<std::string, Objet>» va s'attacher plusieurs fils d'exécution lors
de sa création.

Donc, mis à part l'utilisation d'un pointeur dans le conteneur ou de
modifier la classe afin d'en séparer les processus 'embarassants' de la
création, existe-t-il un mécanisme qui éviterait cet objet temporaire ?


Avatar
Fabien LE LEZ
On Wed, 11 Aug 2004 22:42:47 +0200, David MAREC
:

Si je comprend bien, l'objet temporaire va bel et bien être détruit par tous
les mécanismes idoines (~objet()) et la copie sera aussi créée en
appliquant les mécanismes associés (objet()) ?


Simplifions un peu le code :

std::vector<cFournisseur> v;
v.push_back (cFournisseur("square Georges Brassens"));

Dans l'ordre :

création de l'objet temporaire
(constructeur cFournisseur(char const*) ).
création de l'objet qui se trouve dans le vector<>
(constructeur de copie : cFournisseur(cFournisseur const&) ).
destruction du temporaire
(destructeur ~cFournisseur() ).


--
;-)