Ich versuche herauszufinden, warum der folgende Code nicht funktioniert, und ich gehe davon aus, dass es sich um ein Problem bei der Verwendung von char * als Schlüsseltyp handelt. Ich bin mir jedoch nicht sicher, wie ich ihn beheben kann oder warum er auftritt. Alle anderen Funktionen, die ich (im HL2 SDK) verwende char*
, werden std::string
viele unnötige Komplikationen verursachen.
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;
}
std:string
einmal zu verwenden , und freuen Sie sich danach.char*
als Kartenschlüssel verwenden. Siehe meine Antwort warum.std::string
.Antworten:
Sie müssen der Karte einen Vergleichsfunktor zuweisen, andernfalls wird der Zeiger verglichen, nicht die nullterminierte Zeichenfolge, auf die er zeigt. Im Allgemeinen ist dies immer dann der Fall, wenn Sie möchten, dass Ihr Kartenschlüssel ein Zeiger ist.
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;
quelle
&std::strcmp
den dritten Vorlagenparameter übergebenstrcmp
gibt eine positive, nullte oder negative Ganzzahl zurück. Der Kartenfunktor muss true auf less-than und false zurückgeben, andernfalls.f(a,b) = a<b
nicht erwartetf(a,b) = (-1 if a<b, 1 if a>b, 0 else)
.Sie können es nur verwenden,
char*
wenn Sie absolut 100% sicher sind, dass Sie mit genau denselben Zeigern und nicht mit Zeichenfolgen auf die Karte zugreifen .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 auf die Karte zugreifen, erhalten
s1
Sie einen anderen Ort als mits2
.quelle
Zwei Zeichenfolgen im C-Stil können den gleichen Inhalt haben, sich jedoch an unterschiedlichen Adressen befinden. Und das
map
vergleicht die Zeiger, nicht den Inhalt.Die Kosten für die Konvertierung in sind
std::map<std::string, int>
möglicherweise nicht so hoch wie Sie denken.Aber wenn Sie wirklich
const char*
als Kartenschlüssel verwenden müssen, versuchen Sie:#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;
quelle
Sie können damit arbeiten
std::map<const char*, int>
, dürfen jedoch keine Nicht-const
Zeiger verwenden (beachten Sie dieconst
für den Schlüssel hinzugefügten ), da Sie diese Zeichenfolgen nicht ändern dürfen, während die Zuordnung sie als Schlüssel bezeichnet. (Während eine Karte ihre Schlüssel schützt, indem sie sie erstelltconst
, wird dadurch nur der Zeiger und nicht die Zeichenfolge, auf die sie zeigt, festgelegt.)Aber warum benutzt du nicht einfach
std::map<std::string, int>
? Es funktioniert sofort ohne Kopfschmerzen.quelle
Sie vergleichen mit a
char *
mit einer Zeichenfolge. Sie sind nicht gleich.A
char *
ist ein Zeiger auf ein Zeichen. Letztendlich ist es ein ganzzahliger Typ, dessen Wert als gültige Adresse für a interpretiert wirdchar
.Eine Zeichenfolge ist eine Zeichenfolge.
Der Container funktioniert ordnungsgemäß, jedoch als Container für Paare, in denen der Schlüssel a
char *
und der Wert a istint
.quelle
Wie die anderen sagen, sollten Sie in diesem Fall wahrscheinlich std :: string anstelle eines char * verwenden, obwohl mit einem Zeiger als Schlüssel im Prinzip nichts falsch ist, wenn dies wirklich erforderlich ist.
Ich denke, ein weiterer Grund, warum dieser Code nicht funktioniert, ist, dass Sie, sobald Sie einen verfügbaren Eintrag in der Karte gefunden haben, versuchen, ihn mit demselben Schlüssel (dem Zeichen *) wieder in die Karte einzufügen. Da dieser Schlüssel bereits in Ihrer Karte 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 die Einfügung fehl und der zugeordnete Wert bleibt unverändert. Dann wird es trotzdem gelöscht. Sie müssen es zuerst löschen und dann wieder einfügen.
Auch wenn Sie das Zeichen * in einen std :: string ändern, bleibt dieses Problem bestehen.
Ich weiß, dass dieser Thread ziemlich alt ist und Sie haben inzwischen alles behoben, aber ich habe niemanden gesehen, der diesen Punkt so formuliert hat, um zukünftigen Zuschauern willen, denen ich antworte.
quelle
Es fiel mir schwer, das Zeichen * als Zuordnungsschlüssel zu verwenden, wenn ich versuche, ein Element in mehreren Quelldateien zu finden. Es funktioniert einwandfrei, wenn alle Zugriffe / Suchen in derselben Quelldatei erfolgen, in die die Elemente eingefügt werden. Wenn ich jedoch versuche, mit find in einer anderen Datei auf das Element zuzugreifen, kann ich das Element nicht abrufen, das sich definitiv in der Karte befindet.
Es stellt sich heraus, dass der Grund dafür ist, wie Plabo betonte, dass die Zeiger (jede Kompilierungseinheit hat ihre eigene Konstante char *) NICHT identisch sind, wenn in einer anderen CPP-Datei darauf zugegriffen wird.
quelle
Es gibt kein Problem auf Tastentyp so lange zu verwenden , wie es Vergleich unterstützt (
<
,>
,==
) und Zuordnung.Ein Punkt, der erwähnt werden sollte - berücksichtigen Sie, dass Sie eine Vorlagenklasse verwenden . Als Ergebnis generiert der Compiler zwei verschiedene Instanziierungen für
char*
undint*
. Während der tatsächliche Code von beiden praktisch identisch sein wird.Daher würde ich in Betracht ziehen, a
void*
als Schlüsseltyp zu verwenden und dann nach Bedarf zu gießen. Das ist meine Meinung.quelle
<
. Aber es muss auf eine Weise umgesetzt werden, die hilfreich ist. Es ist nicht mit Zeigern. Moderne Compiler falten identische Vorlageninstanzen. (Ich weiß sicher, dass VC dies tut.) Ich würde es niemals verwendenvoid*
, es sei denn, die Messung hat gezeigt, dass dies viele Probleme löst. Das Aufgeben der Typensicherheit sollte niemals vorzeitig erfolgen.