web-dev-qa-db-fra.com

Utiliser char * comme clé dans std :: map

J'essaie de comprendre pourquoi le code suivant ne fonctionne pas et je suppose qu'il est problématique d'utiliser char * comme type de clé, mais je ne suis pas sûr de savoir comment résoudre ce problème ni pourquoi il se produit. Toutes les autres fonctions que j'utilise (dans le kit SDK HL2) utilisent char*; utiliser std::string va donc entraîner de nombreuses complications inutiles.

std::map<char*, int> g_PlayerNames;

int PlayerManager::CreateFakePlayer()
{
    FakePlayer *player = new FakePlayer();
    int index = g_FakePlayers.AddToTail(player);

    bool foundName = false;

    // Iterate through Player Names and find an Unused one
    for(std::map<char*,int>::iterator it = g_PlayerNames.begin(); it != g_PlayerNames.end(); ++it)
    {
        if(it->second == NAME_AVAILABLE)
        {
            // We found an Available Name. Mark as Unavailable and move it to the end of the list
            foundName = true;
            g_FakePlayers.Element(index)->name = it->first;

            g_PlayerNames.insert(std::pair<char*, int>(it->first, NAME_UNAVAILABLE));
            g_PlayerNames.erase(it); // Remove name since we added it to the end of the list

            break;
        }
    }

    // If we can't find a usable name, just user 'player'
    if(!foundName)
    {
        g_FakePlayers.Element(index)->name = "player";
    }

    g_FakePlayers.Element(index)->connectTime = time(NULL);
    g_FakePlayers.Element(index)->score = 0;

    return index;
}
70
Josh Renwald

Vous devez attribuer un foncteur de comparaison à la carte, sinon le pointeur sera comparé, pas la chaîne terminée par un zéro, elle pointe vers. En général, c'est le cas chaque fois que vous souhaitez que votre clé de carte soit un pointeur.

Par exemple:

struct cmp_str
{
   bool operator()(char const *a, char const *b) const
   {
      return std::strcmp(a, b) < 0;
   }
};

map<char *, int, cmp_str> BlahBlah;
119
GWW

Vous ne pouvez utiliser char* que si vous êtes absolument sûr à 100% que vous allez accéder à la carte avec les mêmes pointeurs exacts, et non des chaînes.

Exemple:

char *s1; // pointing to a string "hello" stored memory location #12
char *s2; // pointing to a string "hello" stored memory location #20

Si vous accédez à map avec s1, vous obtiendrez un emplacement différent de celui d’accéder à s2.

42
Pablo Santa Cruz

Deux chaînes de style C peuvent avoir le même contenu mais être à des adresses différentes. Et cette map compare les pointeurs, pas le contenu.

Le coût de la conversion en std::map<std::string, int> n'est peut-être pas aussi élevé que vous le pensez.

Mais si vous devez vraiment utiliser const char* comme clés de carte, essayez:

#include <functional>
#include <cstring>
struct StrCompare : public std::binary_function<const char*, const char*, bool> {
public:
    bool operator() (const char* str1, const char* str2) const
    { return std::strcmp(str1, str2) < 0; }
};

typedef std::map<const char*, int, StrCompare> NameMap;
NameMap g_PlayerNames;
22
aschepler

Vous comparez en utilisant un char * à en utilisant une chaîne. Ils ne sont pas les mêmes.

Un char * est un pointeur sur un caractère. En fin de compte, il s’agit d’un type entier dont la valeur est interprétée comme une adresse valide pour un char.

Une chaîne est une chaîne.

Le conteneur fonctionne correctement, mais en tant que conteneur pour les paires dans lesquelles la clé est un char * et la valeur est une int.

8
Daniel Daranas

Vous pouvez le faire fonctionner avec std::map<const char*, int>, mais vous ne devez pas utiliser de pointeurs non -const (notez la valeur ajoutée const pour la clé), car vous ne devez pas modifier ces chaînes alors que la mappe les appelle clés. (Bien qu'une carte protège ses clés en les rendant const, cela ne constituerait que le pointeur, pas la chaîne sur laquelle elle pointe.)

Mais pourquoi n'utilisez-vous pas simplement std::map<std::string, int>? Cela fonctionne hors de la boîte sans maux de tête. 

8
sbi

Comme les autres le disent, vous devriez probablement utiliser std :: string au lieu d'un caractère *, bien qu'il n'y ait rien de mal en principe à utiliser un pointeur comme clé si c'est ce qui est vraiment nécessaire.

Je pense qu'une autre raison pour laquelle ce code ne fonctionne pas est qu'une fois que vous avez trouvé une entrée disponible sur la carte, vous essayez de la réinsérer dans la carte avec la même clé (le caractère *). Comme cette clé existe déjà dans votre carte, l'insertion échouera. La norme pour map :: insert () définit ce comportement ... si la valeur de clé existe, l'insertion échoue et la valeur mappée reste inchangée. Ensuite, il est supprimé quand même. Vous devez d'abord le supprimer, puis le réinsérer.

Même si vous changez le caractère * en std :: string, ce problème restera.

Je sais que ce fil est assez ancien et que vous avez déjà tout corrigé, mais je n'ai vu personne le dire, alors je réponds à l'intention des futurs téléspectateurs.

2
Toby Mitchell

J'ai eu du mal à utiliser le caractère * comme clé de la carte lorsque j'essaie de trouver l'élément dans plusieurs fichiers source. Cela fonctionne bien lorsque tous les accès/recherches dans le même fichier source où les éléments sont insérés. Cependant, lorsque j'essaie d'accéder à l'élément en utilisant find dans un autre fichier, je ne parviens pas à obtenir l'élément qui se trouve vraiment à l'intérieur de la carte. 

Il s’avère que la raison est la suivante: Plabo a souligné, les pointeurs (chaque unité de compilation a son propre caractère constant *) ne sont PAS du tout identiques lorsqu’on y accède dans un autre fichier cpp. 

0
Jie Xu