Использование char* в качестве ключа в std::map
Я пытаюсь выяснить, почему следующий код не работает, и я предполагаю, что это проблема с использованием char * в качестве типа ключа, однако я не уверен, как я могу решить его или почему это происходит. Все другие функции, которые я использую (в HL2 SDK), используют 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;
}
10 ответов
Вам нужно дать функтор сравнения на карту, иначе он сравнивает указатель, а не строку с нулевым символом в конце, на которую он указывает. В общем, это так, когда вы хотите, чтобы ключ вашей карты был указателем.
Например:
struct cmp_str
{
bool operator()(char const *a, char const *b) const
{
return std::strcmp(a, b) < 0;
}
};
map<char *, int, cmp_str> BlahBlah;
Вы не можете использовать 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
,
Две строки в стиле 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;
Вы можете заставить его работать с std::map<const char*, int>
, но не должен использовать неconst
указатели (обратите внимание на добавленные const
для ключа), потому что вы не должны изменять эти строки, пока карта называет их ключами. (Пока карта защищает свои ключи, делая их const
, это будет содержать только указатель, а не строку, на которую он указывает.)
Но почему бы тебе просто не использовать std::map<std::string, int>
? Он работает из коробки без головной боли.
Вы сравниваете, используя char *
чтобы использовать строку. Они не то же самое.
char *
это указатель на символ В конечном счете, это целочисленный тип, значение которого интерпретируется как действительный адрес для char
,
Строка - это строка.
Контейнер работает правильно, но как контейнер для пар, в которых ключ является char *
и значение является int
,
Как говорят другие, вам, вероятно, следует использовать std::string вместо char* в этом случае, хотя в принципе нет ничего неправильного с указателем в качестве ключа, если это то, что действительно требуется.
Я думаю, что еще одна причина, по которой этот код не работает, заключается в том, что когда вы находите доступную запись на карте, вы пытаетесь повторно вставить ее в карту с тем же ключом (символ *). Поскольку этот ключ уже существует на вашей карте, вставка не удастся. Стандарт для map::insert() определяет это поведение... если значение ключа существует, вставка завершается неудачно, а сопоставленное значение остается неизменным. Тогда это все равно будет удалено. Вам нужно сначала удалить его, а затем снова вставить.
Даже если вы измените char* на std::string, эта проблема останется.
Я знаю, что эта ветка довольно старая, и вы уже все исправили, но я не видел, чтобы кто-то высказывал эту точку зрения, поэтому ради будущих зрителей я отвечаю.
Вы можете связать лямбду, которая выполняет ту же работу.
#include <map>
#include <functional>
class a{
std::map < const char*, Property*, std::function<bool(const char* a, const char* b)> > propertyStore{
std::bind([](const char* a, const char* b) {return std::strcmp(a,b) < 0;},std::placeholders::_1,std::placeholders::_2)
};
};
std::map<char*,int>
будет использовать значение по умолчанию
std::less<char*,int>
сравнивать
char*
keys, которые будут сравнивать указатели. Но вы можете указать свой собственный класс Compare следующим образом:
class StringPtrCmp {
public:
StringPtrCmp() {}
bool operator()(const char *str1, const char *str2) const {
if (str1 == str2)
return false; // same pointer so "not less"
else
return (strcmp(str1, str2) < 0); //string compare: str1<str2 ?
}
};
std::map<char*, YourType, StringPtrCmp> myMap;
Имейте в виду, что вы должны убедиться, что указатель char* действителен. Я бы посоветовал использовать
std::map<std::string, int>
так или иначе.
Мне было трудно использовать char* в качестве ключа карты, когда я пытался найти элемент в нескольких исходных файлах. Это прекрасно работает, когда все доступ / поиск в одном и том же исходном файле, где элементы вставлены. Однако, когда я пытаюсь получить доступ к элементу, используя find в другом файле, я не могу получить элемент, который определенно находится внутри карты.
Оказывается, причина в том, что, как указал Pablo Santa Cruz, указатели (каждый модуль компиляции имеет свой собственный постоянный символ *) совсем не совпадают, когда к нему обращаются в другом файле cpp.
Нет проблем использовать любой тип ключа, если он поддерживает сравнение (<
, >
, ==
) и назначение.
Один момент, который стоит упомянуть - учтите, что вы используете шаблонный класс. В результате компилятор сгенерирует два разных экземпляра для char*
а также int*
, Принимая во внимание, что фактический код обоих будет фактически идентичен.
Следовательно - я хотел бы рассмотреть возможность использования void*
в качестве типа ключа, а затем приведение при необходимости. Это мое мнение.