it-swarm.com.de

Verwenden von char * als Schlüssel in std :: map

Ich versuche herauszufinden, warum der folgende Code nicht funktioniert, und ich gehe davon aus, dass es ein Problem mit der Verwendung von char * als Schlüsseltyp ist. Ich bin mir jedoch nicht sicher, wie ich es lösen kann oder warum es auftritt. Alle anderen Funktionen, die ich verwende (im HL2-SDK), verwenden char*, so dass die Verwendung von std::string viele unnötige Komplikationen verursachen wird.

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

Sie müssen der Karte einen Vergleichsfunktor zuweisen, ansonsten wird der Zeiger und nicht die nullterminierte Zeichenfolge, auf die er zeigt, verglichen. Im Allgemeinen ist dies der Fall, wenn Sie Ihren Kartenschlüssel als Zeiger verwenden möchten.

Zum Beispiel:

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

Sie können char* nicht verwenden, es sei denn, Sie sind zu 100% sicher, dass Sie mit den exakt gleichen Zeigern auf die Karte zugreifen, nicht mit Strings.

Beispiel:

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

Wenn Sie mit s1 auf die Karte zugreifen, erhalten Sie einen anderen Ort als den Zugriff mit s2.

42

Zwei Zeichenketten im C-Stil können den gleichen Inhalt haben, jedoch unterschiedliche Adressen haben. Und dieser map vergleicht die Zeiger, nicht den Inhalt.

Die Kosten für die Konvertierung in std::map<std::string, int> sind möglicherweise nicht so hoch wie Sie denken.

Wenn Sie jedoch wirklich const char* als Kartenschlüssel verwenden müssen, versuchen Sie Folgendes:

#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

Sie vergleichen einen char * mit einem String. Sie sind nicht gleich.

Ein char * ist ein Zeiger auf ein Zeichen. Letztendlich handelt es sich um einen Integer-Typ, dessen Wert als gültige Adresse für eine char interpretiert wird.

Eine Zeichenfolge ist eine Zeichenfolge.

Der Container funktioniert ordnungsgemäß, jedoch als Container für Paare, bei denen der Schlüssel ein char * und der Wert eine int ist.

8
Daniel Daranas

Sie können es mit std::map<const char*, int> zum Laufen bringen, es dürfen jedoch keine Nicht -const-Zeiger verwendet werden (beachten Sie das hinzugefügte const für den Schlüssel), da Sie diese Zeichenfolgen nicht ändern dürfen, während die Karte sie als Schlüssel verwendet. (Während eine Map ihre Schlüssel schützt, indem sie const erstellt wird, würde dies nur den Pointer und nicht die Zeichenfolge enthalten, auf die sie verweist.)

Aber warum benutzt du nicht einfach std::map<std::string, int>? Es funktioniert ohne Kopfschmerzen. 

8
sbi

Wie die anderen sagen, sollten Sie in diesem Fall wahrscheinlich std :: string anstelle von char * verwenden, obwohl mit einem Zeiger als Schlüssel prinzipiell nichts falsch ist, wenn dies wirklich erforderlich ist.

Ich denke, ein weiterer Grund, warum dieser Code nicht funktioniert, besteht darin, dass Sie, sobald Sie einen verfügbaren Eintrag in der Karte gefunden haben, versuchen, ihn mit demselben Schlüssel (das Zeichen *) erneut in die Karte einzufügen. Da dieser Schlüssel bereits in Ihrer Map vorhanden ist, schlägt das Einfügen fehl. Der Standard für map :: insert () definiert dieses Verhalten ... Wenn der Schlüsselwert vorhanden ist, schlägt das Einfügen fehl und der zugeordnete Wert bleibt unverändert. Dann wird es trotzdem gelöscht. Sie müssen es zuerst löschen und dann erneut einfügen.

Selbst wenn Sie das Zeichen * in einen std :: string ändern, bleibt dieses Problem bestehen.

Ich weiß, dass dieser Thread ziemlich alt ist, und du hast es inzwischen behoben, aber ich habe niemanden gesehen, der diesen Punkt anspricht. Deshalb beantworte ich die zukünftigen Zuschauer.

2
Toby Mitchell

Ich hatte eine harte Zeit mit dem Zeichen char * als Kartenschlüssel, wenn ich versuche, ein Element in mehreren Quelldateien zu finden. Es funktioniert gut, wenn alle Zugriffe/Suchvorgänge innerhalb derselben Quelldatei erfolgen, in der die Elemente eingefügt werden. Wenn ich jedoch mit find in einer anderen Datei auf das Element zugreifen möchte, kann ich das Element nicht abrufen, das sich definitiv in der Map befindet. 

Es stellt sich heraus, dass der Grund darin liegt, dass Plabo darauf hingewiesen wird, dass die Zeiger (jede Kompilierungseinheit hat ihr eigenes konstantes Zeichen *) NICHT die gleichen sind, wenn sie in einer anderen CPP-Datei aufgerufen werden. 

0
Jie Xu