Использование char * в качестве ключа в std:: map
Я пытаюсь понять, почему следующий код не работает, и я предполагаю, что это проблема с использованием char * в качестве типа ключа, однако я не уверен, как я могу его решить или почему встречающаяся. Все другие функции, которые я использую (в SDK HL2), используют char*
, поэтому использование std::string
вызовет массу ненужных осложнений.
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;
}
Ответы
Ответ 1
Вам нужно дать функтор сравнения на карту, иначе он сравнивает указатель, а не строку с нулевым символом в конце, на которую он указывает. В общем, это так, когда вы хотите, чтобы ключ вашей карты был указателем.
Например:
struct cmp_str
{
bool operator()(char const *a, char const *b) const
{
return std::strcmp(a, b) < 0;
}
};
map<char *, int, cmp_str> BlahBlah;
Ответ 2
Вы не можете использовать char*
, если вы абсолютно на 100% уверены, что собираетесь получить доступ к карте с помощью точных указателей, а не строк.
Пример:
char *s1; // pointing to a string "hello" stored memory location #12
char *s2; // pointing to a string "hello" stored memory location #20
Если вы получаете доступ к карте с помощью s1
, вы получите другое местоположение, чем доступ к ней с помощью s2
.
Ответ 3
Две строки в стиле C могут иметь одинаковое содержимое, но быть разными адресами. И что map
сравнивает указатели, а не содержимое.
Стоимость преобразования в std::map<std::string, int>
может быть не такой, как вы думаете.
Но если вам действительно нужно использовать const char*
в качестве клавиш карты, попробуйте:
#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;
Ответ 4
Вы сравниваете использование char *
с использованием строки. Они не совпадают.
A char *
является указателем на char. В конечном счете, это целочисленный тип, значение которого интерпретируется как действительный адрес для char
.
Строка - это строка.
Контейнер работает правильно, но в качестве контейнера для пар, в которых ключ является char *
, а значение - int
.
Ответ 5
Вы можете заставить его работать с std::map<const char*, int>
, но не должны использовать указатели не const
(обратите внимание на добавленный const
для ключа), потому что вы не должны изменять эти строки, пока карта относится к ним как к клавишам, (Пока карта защищает свои ключи, создавая их const
, это будет только указывать указатель, а не строку, на которую указывает.)
Но почему бы вам просто не использовать std::map<std::string, int>
? Он работает из коробки без головных болей.
Ответ 6
Как говорят другие, вы должны использовать std::string вместо char * в этом случае, хотя в принципе нет ничего неправильного с указателем в качестве ключа, если это действительно необходимо.
Я думаю, что еще одна причина, по которой этот код не работает, заключается в том, что как только вы найдете доступную запись на карте, вы пытаетесь вставить ее в карту с тем же ключом (char *). Поскольку этот ключ уже существует на вашей карте, вставка не будет выполнена. Стандарт для map:: insert() определяет это поведение... если значение ключа существует, вставка терпит неудачу, и отображаемое значение остается неизменным. Тогда он все равно удаляется. Сначала вам нужно удалить его, а затем снова вставить.
Даже если вы измените char * на std::string, эта проблема останется.
Я знаю, что эта ветка довольно старая, и вы все это исправили, но я не видел, чтобы кто-то делал это так ради будущих зрителей, на которые я отвечаю.
Ответ 7
Нелегко было использовать char * в качестве ключа карты, когда я пытаюсь найти элемент в нескольких исходных файлах. Он отлично работает при доступе/поиске в том же исходном файле, в который вставлены элементы. Однако, когда я пытаюсь получить доступ к элементу, используя find в другом файле, я не могу получить элемент, который определенно находится внутри карты.
Оказывается, причина в том, что Plabo указал, что указатели (каждая единица компиляции имеет свою собственную константу char *) совсем не совпадают, когда он доступен в другом файле cpp.
Ответ 8
Нет проблем с использованием любого типа ключа, если он поддерживает сравнение (<
, >
, ==
) и назначение.
Один пункт, который следует упомянуть - учтите, что вы используете класс шаблон. В результате компилятор будет генерировать два разных экземпляра для char*
и int*
. В то время как фактический код обоих будет практически идентичным.
Следовательно - я бы рассмотрел использование void*
в качестве ключевого типа, а затем, при необходимости, внесение.
Это мое мнение.