Использование 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* в качестве ключевого типа, а затем, при необходимости, внесение. Это мое мнение.