Using char* as a key in std::map

2019-01-02 17:52发布

I am trying to figure out why the following code is not working, and I am assuming it is an issue with using char* as the key type, however I am not sure how I can resolve it or why it is occuring. All of the other functions I use (in the HL2 SDK) use char* so using std::string is going to cause a lot of unnecessary complications.

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;
}

标签: c++ map stdmap
8条回答
高级女魔头
2楼-- · 2019-01-02 18:22

You are comparing using a char * to using a string. They are not the same.

A char * is a pointer to a char. Ultimately, it is an integer type whose value is interpreted as a valid address for a char.

A string is a string.

The container works correctly, but as a container for pairs in which the key is a char * and the value is an int.

查看更多
只若初见
3楼-- · 2019-01-02 18:23

You need to give a comparison functor to the map otherwise it's comparing the char* pointer not the string. In general, this is the case anytime you want your map key to be a pointer.

ie.

struct cmp_str
{
   bool operator()(char const *a, char const *b) const
   {
      return std::strcmp(a, b) < 0;
   }
};

map<char *, int, cmp_str> BlahBlah;

EDIT: Acutally disregard my edit, the functor is easier to use.

查看更多
零度萤火
4楼-- · 2019-01-02 18:23

There's no problem to use any key type as long as it supports comparison (<, >, ==) and assignment.

One point that should be mentioned - take into account that you're using a template class. As the result compiler will generate two different instantiations for char* and int*. Whereas the actual code of both will be virtually identical.

Hence - I'd consider using a void* as a key type, and then casting as necessary. This is my opinion.

查看更多
高级女魔头
5楼-- · 2019-01-02 18:34

As the others say, you should probably use std::string instead of a char* in this case although there is nothing wrong in principle with a pointer as a key if that's what is really required.

I think another reason this code isn't working is because once you find an available entry in the map you attempt to reinsert it into the map with the same key (the char*). Since that key already exists in your map, the insert will fail. The standard for map::insert() defines this behaviour...if the key value exists the insert fails and the mapped value remains unchanged. Then it gets deleted anyway. You'd need to delete it first and then reinsert.

Even if you change the char* to a std::string this problem will remain.

I know this thread is quite old and you've fixed it all by now but I didn't see anyone making this point so for the sake of future viewers I'm answering.

查看更多
看淡一切
6楼-- · 2019-01-02 18:36

You can get it working with std::map<const char*, int>, but must not use non-const pointers (note the added const for the key), because you must not change those strings while the map refers to them as keys. (While a map protects its keys by making them const, this would only constify the pointer, not the string it points to.)

But why don't you simply use std::map<std::string, int>? It works out of the box without headaches.

查看更多
一个人的天荒地老
7楼-- · 2019-01-02 18:38

You can't use char* unless you are absolutely 100% sure you are going to access the map with the exact same pointers, not strings.

Example:

char *s1; // pointing to a string "hello" stored memory location #12
char *s2; // pointing to a string "hello" stored memory location #20

If you access map with s1 you will get a different location than accessing it with s2.

查看更多
登录 后发表回答