Estoy tratando de averiguar por qué el siguiente código no funciona y supongo que es un problema con el uso de char * como tipo de clave, sin embargo, no estoy seguro de cómo puedo resolverlo o por qué ocurre. Todas las demás funciones que uso (en el SDK de HL2) las utilizo, char*
por lo que su uso std::string
va a causar muchas complicaciones innecesarias.
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
una vez y sea feliz después.char*
como clave de mapa. Vea mi respuesta por qué.std::string
.Respuestas:
Debe proporcionar un functor de comparación al mapa, de lo contrario, está comparando el puntero, no la cadena terminada en nulo a la que apunta. En general, este es el caso siempre que desee que la clave del mapa sea un puntero.
Por ejemplo:
struct cmp_str { bool operator()(char const *a, char const *b) const { return std::strcmp(a, b) < 0; } }; map<char *, int, cmp_str> BlahBlah;
fuente
&std::strcmp
como tercer parámetro de plantillastrcmp
devuelve un número entero positivo, cero o negativo. El functor del mapa debe devolver verdadero en menor que y falso en caso contrario.f(a,b) = a<b
, nof(a,b) = (-1 if a<b, 1 if a>b, 0 else)
.No puede usar a
char*
menos que esté absolutamente 100% seguro de que va a acceder al mapa con exactamente los mismos punteros , no cadenas.Ejemplo:
char *s1; // pointing to a string "hello" stored memory location #12 char *s2; // pointing to a string "hello" stored memory location #20
Si accede al mapa con
s1
, obtendrá una ubicación diferente a la que accede cons2
.fuente
Dos cadenas de estilo C pueden tener el mismo contenido pero estar en direcciones diferentes. Y eso
map
compara los punteros, no el contenido.Es posible que el costo de conversión a
std::map<std::string, int>
no sea tanto como cree.Pero si realmente necesita usar
const char*
como claves de mapa, intente:#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;
fuente
Puede hacer que funcione
std::map<const char*, int>
, pero no debe usar noconst
punteros (tenga en cuenta lo agregadoconst
para la clave), porque no debe cambiar esas cadenas mientras el mapa se refiere a ellas como claves. (Si bien un mapa protege sus claves al hacerlasconst
, esto solo constificaría el puntero , no la cadena a la que apunta).Pero, ¿por qué no lo usas simplemente
std::map<std::string, int>
? Funciona de inmediato sin dolores de cabeza.fuente
Estás comparando el uso de un
char *
con el uso de una cadena. Ellos no son los mismos.A
char *
es un puntero a un char. En última instancia, es un tipo entero cuyo valor se interpreta como una dirección válida para achar
.Una cuerda es una cuerda.
El contenedor funciona correctamente, pero como contenedor de pares en los que la clave es a
char *
y el valor es anint
.fuente
Como dicen los demás, probablemente debería usar std :: string en lugar de char * en este caso, aunque en principio no hay nada de malo con un puntero como clave si eso es lo que realmente se requiere.
Creo que otra razón por la que este código no funciona es porque una vez que encuentra una entrada disponible en el mapa, intenta reinsertarla en el mapa con la misma clave (el carácter *). Dado que esa clave ya existe en su mapa, la inserción fallará. El estándar para map :: insert () define este comportamiento ... si el valor clave existe, la inserción falla y el valor mapeado permanece sin cambios. Luego se elimina de todos modos. Primero deberá eliminarlo y luego reinsertarlo.
Incluso si cambia el char * a std :: string, este problema permanecerá.
Sé que este hilo es bastante antiguo y ya lo has arreglado todo, pero no vi a nadie haciendo este punto, así que por el bien de los futuros espectadores, estoy respondiendo.
fuente
Tuve dificultades para usar el char * como clave de mapa cuando trato de encontrar el elemento en varios archivos de origen. Funciona bien cuando todos los accesos / búsquedas están dentro del mismo archivo fuente donde se insertan los elementos. Sin embargo, cuando intento acceder al elemento usando buscar en otro archivo, no puedo obtener el elemento que definitivamente está dentro del mapa.
Resulta que la razón es que, como señaló Plabo , los punteros (cada unidad de compilación tiene su propio carácter constante *) NO son los mismos cuando se accede a ellos en otro archivo cpp.
fuente
No hay ningún problema para utilizar cualquier tipo de clave, siempre que soporta la comparación (
<
,>
,==
) y asignación.Un punto que debe mencionarse: tenga en cuenta que está utilizando una clase de plantilla . Como resultado, el compilador generará dos instancias diferentes para
char*
yint*
. Mientras que el código real de ambos será prácticamente idéntico.Por lo tanto, consideraría usar a
void*
como tipo de clave y luego lanzar según sea necesario. Esta es mi opinión.fuente
<
. Pero necesita implementar eso de una manera que sea útil. No es con punteros. Los compiladores modernos plegarán instancias de plantilla idénticas. (Sé con certeza que VC hace esto). Nunca lo usaríavoid*
, a menos que la medición mostrara esto para resolver muchos problemas. El abandono de la seguridad de tipos nunca debe hacerse de forma prematura.