IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage C++ Discussion :

[map] Clés et valeurs qui se font écrasées.


Sujet :

Langage C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre actif
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    43
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 43
    Par défaut [map] Clés et valeurs qui se font écrasées.
    Salut,

    J'ai recommencer à coder en C++ il n'y a pas très longtemps, et j'arrive rapidement à un problème (que j'ai été capable de reproduire). Le problème est : Lorsqu'une même variable est utilisée pour initialiser plusieurs entrées dans une variable map<const char*,string>, il se produit un comportement bizarre et plutôt inattendu.

    Voici un code que j'ai fait pour simplifier le problème :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    #include <iostream>
    #include <string>
    #include <map>
     
    using namespace std;
     
    map<const char*, string> *Map = new map<const char*, string>( );
     
    void Set( const char* Name, const char* Value )
    {
    	cout << Name << "=" << Value << endl;
    	(*Map)[Name] = Value;
    }
     
    int main()
    {
    	string Std;
    	string Std2;
     
    	for( int i = 1 ; i <= 8 ; i++ )
    	{
    		Std = "Variable"; Std.resize( i );
    		Std2 = "Variable"; Std2.resize( i );
     
    		Set( Std.c_str( ), Std2.c_str( ) );
    	}
     
    	cout << "Variable dump : " << endl;
    	for( map<const char*,string>::iterator it = Map->begin( ) ; it != Map->end( ) ; it++ )
    	{
    		cout << it->first << " = " << it->second << endl;
    	}
     
    	delete Map;
     
    	return 0;
    }
    Output :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    V=V
    Va=Va
    Var=Var
    Vari=Vari
    Varia=Varia
    Variab=Variab
    Variabl=Variabl
    Variable=Variable
    Variable dump :
    Variable = Variable
    Avez-vous déjà rencontré ce genre de problème ? Savez-vous comment le régler ?

    Merci d'avance pour vos réponses .

  2. #2
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Par défaut
    Ici Name est de type const char * et contient donc non pas une chaîne de caractère mais une adresse (d'une chaîne certes, mais une adresse tout de même). Donc toute la gestion de la clé de ta map, et particulier les tests d'équivalence, se font sur l'adresse contenu dans Name et non sur ce qui est situé à cette adresse.

    Or vu la façon dont tu construis ici Name pour la passer à ta fonction Set, l'adresse est la même à chaque itération. Tu as donc un seul élément dans ta map dont la clé est cette fameuse adresse, adresse qui lors de la dernière itération pointe sur la chaîne "Variable".
    Utilise des std::string plutôt que des chaînes C-style dans ta std::map.

    Au passage:
    • Quel est l'intérêt de l'allocation dynamique de la std::map ici ?
    • Pourquoi cette map est-elle une variable globale ?
    • Ce n'est généralement pas une bonne idée de stocker l'adresse retourné par la méthode c_str.

  3. #3
    Membre chevronné

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    426
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Puy de Dôme (Auvergne)

    Informations forums :
    Inscription : Octobre 2008
    Messages : 426
    Par défaut
    Salut,

    Il semble que le problème vienne du fait que dans la fonction Set, Name et Value sont des pointeurs!
    Au premier appel de Set, tu crée un premier élément...
    ...Aux appels suivant de Set, tu ne fait que modifier la clé de cet élément et sa valeur associée! Tu ne crée pas un nouvel élément!!!

    Il ne faut pas écrire : mais
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    (*Map)[CopieDeName] = CopieDeValue;
    Et là ça marche tout de suite mieux!

    Oups, j'ai pas répondu assez vite...

  4. #4
    Membre actif
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    43
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 43
    Par défaut
    Merci de vos réponses.

    Par contre, j'ai déjà tenté de le faire avec une variable différente chaque fois, mais sans succès (Il suffit de mettre les déclarations des deux strings dans la boucle et de recompiller, il y a toujours une erreur même si la variable est différente).

    Je tiens à utiliser les const char*, savez-vous comment je pourrais les utiliser quand même ?

    Je me doutais que c'était le problème, donc j'ai tenté d'utiliser des fonctions comme strcpy, mais sans succès non plus.

    Pour les questions de gl :
    - Il n'y a pas vraiment de raison, mais j'aime mieux l'utiliser comme ça (En même temps, ça me rappelle les notions liées au C++).
    - C'est une variable globale, car, initialement, le problème se produisait dans une classe (j'ai écris ce code pour simplifier le problème).
    - Ok, merci pour l'information, mais pourquoi donc ? (Le std::string le détruit automatiquement ?)

    EDIT : C'est dommage, je croyais que la classe gérait ce genre de détails seule :S (Se garder une copie interne d'une chaine passée en argument).

  5. #5
    Rédacteur/Modérateur
    Avatar de JolyLoic
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Août 2004
    Messages
    5 463
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 50
    Localisation : France, Yvelines (Île de France)

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Août 2004
    Messages : 5 463
    Par défaut
    La classe map gère correctement le fait de se garder une copie interne des éléments (clef et valeur). Il faut juste pour ça que le type soit copiable, avec une bonne sémantique de copie. Le seul problème est que tu utilises comme type de clef un type pour lequel la notion de copie n'est pas correctement définie : J'ai nommé l'infâme char*. En effet, char* étant un pointeur, le copier ne copie que le pointeur, pas les données copiées, d'où ton impression d'écrasement.

    La solution : Utilises un vrai type copiable comme clef, à savoir std::string. Tu tiens peut-être à utiliser des char*, mais ils ne sont pas vraiment compatible avec std::map (on peut quand même se débrouiller en gérant soi même des copies avant de passer les char* à map, et en définissant un comparateur qui va bien à la map, et en faisant gaffe à désallouer la mémoire à chaque fois que tu enlèves un élément de la map, et en faisant gaffe que l'opérateur [] peut ou pas générer une nouvelle clef, et que selon le cas tu devras désallouer la chaîne ou pas et.....)

    Franchement, pour utiliser char* dans une map, il faudrait un motif beaucoup, beaucoup plus impérieux que "je tiens à".
    Ma session aux Microsoft TechDays 2013 : Développer en natif avec C++11.
    Celle des Microsoft TechDays 2014 : Bonnes pratiques pour apprivoiser le C++11 avec Visual C++
    Et celle des Microsoft TechDays 2015 : Visual C++ 2015 : voyage à la découverte d'un nouveau monde
    Je donne des formations au C++ en entreprise, n'hésitez pas à me contacter.

  6. #6
    Membre actif
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    43
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 43
    Par défaut
    Ok, je crois que je vais m'en tenir à l'utilisation de std::string (C'est vrai que, tant qu'à utiliser la STL, vaut mieux l'utiliser au complet x)).

    C'est dommage, je croyais que ça aurait été plus efficace d'avoir un const char*, comme les clés ne subissaient aucunes modification/operation, mais on dirait bien que ça serait galérer pour rien

    Merci beaucoup de vos réponses

  7. #7
    gl
    gl est déconnecté
    Rédacteur

    Homme Profil pro
    Inscrit en
    Juin 2002
    Messages
    2 165
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France, Isère (Rhône Alpes)

    Informations forums :
    Inscription : Juin 2002
    Messages : 2 165
    Par défaut
    Citation Envoyé par Totony Voir le message
    - Ok, merci pour l'information, mais pourquoi donc ? (Le std::string le détruit automatiquement ?)
    c_str retourne l'adresse d'un buffer interne à la classe string contenant la chaîne dans un format "C".
    Or dans différents cas - destruction du string (qui libère ce buffer), augmentation de la taille de la chaîne qui conduit à une réallocation des buffers internes, ... - l'adresse retourné devient invalide. Il ne faut donc pas stocker cette adresse.

  8. #8
    Membre actif
    Profil pro
    Inscrit en
    Novembre 2010
    Messages
    43
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Novembre 2010
    Messages : 43
    Par défaut
    Oh, c'est vrai, je viens d'aller vérifier la doc C++, j'avais loupé cette ligne

    Merci de l'info

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. [Mapping] Mapping avec une table qui contient 2 clés primaires
    Par San_mary dans le forum Hibernate
    Réponses: 13
    Dernier message: 08/09/2014, 10h51
  2. Map associant 2 clés à une valeur
    Par Invité dans le forum Collection et Stream
    Réponses: 4
    Dernier message: 21/05/2009, 10h47
  3. Réponses: 2
    Dernier message: 04/08/2005, 12h26
  4. Trier un std::map selon les valeurs plutot que les clés
    Par dj.motte dans le forum SL & STL
    Réponses: 2
    Dernier message: 13/11/2004, 21h54
  5. Réponses: 7
    Dernier message: 26/10/2004, 11h02

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo