Использование 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;
}
8 67

8 ответов:

вам нужно дать функтор сравнения для карты, иначе он сравнивает указатель char*, а не строку. В общем, это происходит в любое время, когда вы хотите, чтобы ваш ключ карты был указателем.

ie.

struct cmp_str
{
   bool operator()(char const *a, char const *b)
   {
      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, это будет только constify указатель, а не строка, на которую он указывает.)

но почему бы вам просто не использовать std::map<std::string, int>? Он работает из коробки без головных болей.

вы сравниваете с помощью char * с помощью строки. Это не одно и то же.

A char * - это указатель на char. В конечном счете, это целочисленный тип, значение которого интерпретируется как действительный адрес char.

строки.

контейнер работает правильно, но как контейнер для пар, в которых ключевым является char * и значение элемента int.

Как говорят другие, вы, вероятно, должны использовать std::string вместо char* в этом случае, хотя нет ничего плохого в принципе с указателем в качестве ключа, если это то, что действительно требуется.

Я думаю, что еще одна причина, по которой этот код не работает, заключается в том, что как только вы найдете доступную запись на карте, вы попытаетесь снова вставить ее в карту с тем же ключом (char*). Поскольку этот ключ уже существует в вашей карте, вставка завершится ошибкой. Стандарт для map:: insert() определяет такое поведение...если значение ключа существует, вставка завершается ошибкой, и сопоставленное значение остается неизменным. Тогда он будет удален в любом случае. Вам нужно будет сначала удалить его, а затем снова вставить.

даже если вы измените символ* на std:: string, эта проблема останется.

Я знаю, что эта тема довольно старая, и вы уже все исправили, но я не видел, чтобы кто-то делал это, поэтому ради будущих зрителей я отвечаю.

было трудно использовать char* в качестве ключа карты, когда я пытаюсь найти элемент в нескольких исходных файлах. Он отлично работает, когда все доступ/поиск в том же исходном файле, где вставляются элементы. Однако, когда я пытаюсь получить доступ к элементу с помощью найти в другом файле, я не могу получить элемент, который определенно внутри карты.

оказывается причина в том, как Plabo указал, указатели (каждая единица компиляции имеет свою собственную константу char*) совсем не то же самое, когда он доступен в другом файле cpp.

нет никаких проблем, чтобы использовать любой тип ключа, пока он поддерживает сравнение (<,>,==) и назначение.

один момент, который следует упомянуть - примите во внимание, что вы используете шаблон класса. В результате компилятор будет генерировать два разных экземпляра для char* и int*. Тогда как на самом деле код оба будут практически идентичны.

следовательно-я бы рассмотрел использование void* как ключевой тип, а потом кастинг по мере необходимости. Это мое мнение.