在 std::map 中使用 char* 作为键

发布于 2024-10-01 11:02:08 字数 1314 浏览 5 评论 0原文

我试图找出为什么以下代码不起作用,并且我假设这是使用 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;
}

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(10

赤濁 2024-10-08 11:02:08

您需要为映射提供一个比较函子,否则它将比较指针,而不是它指向的以空结尾的字符串。一般来说,只要您希望地图键成为指针,就会出现这种情况。

例如:

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

map<char *, int, cmp_str> BlahBlah;

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

For example:

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

map<char *, int, cmp_str> BlahBlah;
我的痛♀有谁懂 2024-10-08 11:02:08

除非您绝对 100% 确定将使用完全相同的指针而不是字符串来访问地图,否则不能使用 char*

示例:

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

如果您使用 s1 访问地图,您将获得与使用 s2 访问地图不同的位置。

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.

傲影 2024-10-08 11:02:08

两个 C 样式字符串可以具有相同的内容,但位于不同的地址。该 map 比较的是指针,而不是内容。

转换为 std::map 的成本可能没有您想象的那么多。

但如果您确实需要使用 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;

Two C-style strings can have equal contents but be at different addresses. And that map compares the pointers, not the contents.

The cost of converting to std::map<std::string, int> may not be as much as you think.

But if you really do need to use const char* as map keys, try:

#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;
不气馁 2024-10-08 11:02:08

您可以使其与 std::map 一起使用,但不得使用非 const 指针(请注意添加的 const 作为键),因为当映射将它们引用为键时,您不得更改这些字符串。 (虽然映射通过将其设置为常量来保护其键,但这只会使指针稳定,而不是它指向的字符串。)

但是为什么不简单地使用 <代码>std::map?它开箱即用,不会让人头疼。

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.

小矜持 2024-10-08 11:02:08

您正在将使用 char * 与使用字符串进行比较。它们不一样。

char * 是指向 char 的指针。最终,它是一个整数类型,其值被解释为 char 的有效地址。

字符串就是字符串。

该容器工作正常,但作为键为 char * 且值为 int 的对的容器。

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.

浮生未歇 2024-10-08 11:02:08

正如其他人所说,在这种情况下,您可能应该使用 std::string 而不是 char* ,尽管如果确实需要的话,原则上将指针作为键没有任何问题。

我认为此代码不起作用的另一个原因是,一旦您在地图中找到可用条目,您就会尝试使用相同的键(char*)将其重新插入到地图中。由于该键已存在于您的映射中,因此插入将失败。 map::insert() 的标准定义了这种行为...如果键值存在,则插入失败并且映射的值保持不变。然后无论如何它都会被删除。您需要先删除它,然后重新插入。

即使您将 char* 更改为 std::string 这个问题仍然存在。

我知道这个帖子已经很老了,你现在已经把它全部修好了,但我没有看到有人提出这一点,所以为了未来的观众,我正在回答。

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.

白云不回头 2024-10-08 11:02:08

当我尝试在多个源文件中查找元素时,很难使用 char* 作为映射键。当所有访问/查找都在插入元素的同一源文件中时,它工作得很好。但是,当我尝试使用另一个文件中的 find 访问该元素时,我无法获取绝对位于地图内的元素。

事实证明,原因正如 Plabo 指出的那样,指针(每个编译单元都有自己的常量 char *) 在另一个 cpp 文件中访问时完全不一样。

Had a hard time using the char* as the map key when I try to find element in multiple source files. It works fine when all the accessing/finding within the same source file where the elements are inserted. However, when I try to access the element using find in another file, I am not able to get the element which is definitely inside the map.

It turns out the reason is as Plabo pointed out, the pointers (every compilation unit has its own constant char*) are NOT the same at all when it is accessed in another cpp file.

梦幻的味道 2024-10-08 11:02:08

std::map 将使用默认的 std::less 来比较 char* 键,这将进行指针比较。但是您可以像这样指定您自己的 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::map<char*,int> will use the default std::less<char*,int> to compare char* keys, which will do a pointer comparison. But you can specify your own Compare class like this:

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;

Bear in mind that you have to make sure that the char* pointer are valid.
I would advice to use std::map<std::string, int> anyway.

夏尔 2024-10-08 11:02:08

您可以绑定执行相同工作的 lambda。

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

You can bind a lambda that does the same job.

#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)
    };
};
一抹淡然 2024-10-08 11:02:08

使用任何键类型都没有问题,只要它支持比较(<>==)和赋值即可。

应该提到的一点 - 考虑到您正在使用模板类。结果编译器将为 char*int* 生成两个不同的实例。而两者的实际代码几乎是相同的。

因此 - 我会考虑使用 void* 作为键类型,然后根据需要进行转换。
这是我的意见。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文