87

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

9 回答 9

151

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

例如:

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

map<char *, int, cmp_str> BlahBlah;
于 2010-11-11T18:07:38.157 回答
48

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

于 2010-11-11T18:06:10.840 回答
25

两个 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;
于 2010-11-11T18:10:26.120 回答
9

您可以使用它std::map<const char*, int>,但不能使用非const指针(注意const为键添加的),因为当映射将它们称为键时,您不能更改这些字符串。(虽然地图通过制作它们来保护它的键const,但这只会构成指针,而不是它指向的字符串。)

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

于 2010-11-11T18:15:40.733 回答
8

您正在比较使用 achar *和使用字符串。它们是不相同的。

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

字符串是字符串。

该容器可以正常工作,但作为键为 achar *且值为 an 的对的容器int

于 2010-11-11T18:07:21.883 回答
2

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

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

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

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

于 2012-04-16T13:47:10.647 回答
0

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

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

于 2015-01-07T20:05:58.967 回答
0

std::map<char*,int>将使用默认值std::less<char*,int>来比较char*键,这将进行指针比较。但是您可以像这样指定自己的比较类:

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>无论如何都要使用。

于 2020-12-18T10:43:56.260 回答
-5

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

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

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

于 2010-11-11T18:10:33.040 回答