2010-11-11 119 views
61

我想弄清楚为什么下面的代码不工作,并且我假设它是使用char *作为键类型的问题,但是我不确定我如何解决它或为什么发生。我使用的所有其他功能(在HL2 SDK中)使用char*,因此使用std::string会导致很多不必要的复杂情况。使用char *作为std :: map中的键

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

有时候做正确的事情一开始就会受伤。改变你的代码使用'std:string'一次,然后开心。 – 2010-11-11 18:06:42

+1

有什么样的并发症?有一个从char *到std :: string的隐式转换。 – tenfour 2010-11-11 18:07:50

+0

您不得使用'char *'作为映射键。请参阅[我的答案](http://stackoverflow.com/questions/4157687/using-char-as-a-key-in-stdmap/4157811#4157811)为什么。 – sbi 2010-11-11 18:17:42

回答

108

你需要给映射赋予一个比较函子,否则它会比较char *指针而不是字符串。一般情况下,任何时候你都希望你的map key是一个指针。

即。

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

map<char *, int, cmp_str> BlahBlah; 

编辑:Acutally无视我的编辑,仿函数更容易使用。

+2

实际上他可以通过'&std :: strcmp'作为第三个模板参数 – 2010-11-11 18:08:41

+20

否,'strcmp'返回一个正整数,零整数或负整数。地图仿函数需要返回true,否则返回false。 – aschepler 2010-11-11 18:11:30

+4

@Armen:我不认为它有效,因为第三个模板参数需要类似于'f(a,b)= a b, 0 else)'。 – kennytm 2010-11-11 18:12:12

41

不能使用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

非常好的解释。 – 2010-11-11 18:09:44

8

您在比较是否使用char *来使用字符串。他们不一样。

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

字符串是一个字符串。

容器能够正常工作,但作为密钥对的容器,其密钥为char *,值为int

+1

指针不需要是长整数。有一些平台(如win64,如果你曾经听说过:-)),其中一个长整数比一个指针小,我相信也有更多的晦涩难懂的平台,其中指针和整数被加载到不同的寄存器中,其他方法。 C++只要求指针可以转换成一些整数类型并返回;请注意,这并不意味着您可以将任何足够小的整数转换为指针,而只是转换指针所获得的整数。 – 2012-04-16 14:00:47

+0

@ChristopherCreutzig,谢谢你的评论。我相应地编辑了我的答案。 – 2012-04-16 14:14:35

21

两个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; 
+0

感谢您的信息。根据我的经验,我强烈建议转换为std :: string。 – user2867288 2014-07-27 02:17:31

-5

有没有问题,只要它支持比较(<>==)和分配使用任何密钥类型。

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

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

+6

他们的钥匙只需要支持'<'。但它需要以有用的方式实施。这不是用指针。现代编译器将折叠相同的模板实例。 (我知道VC肯定会这样做)。我绝不会使用'void *',除非测量显示这可以解决很多问题。 __Abandoning类型安全不应该过早地完成.__ – sbi 2010-11-11 18:20:16

8

你可以得到它与std::map<const char*, int>工作,但不得使用非const指针(注意,重点增加const),因为你不能改变这些字符串,而地图是指他们作为密钥。 (虽然地图保护,使它们const它的钥匙,这只会constify的指针,不是指向字符串)。

但是你为什么不干脆用std::map<std::string, int>?它可以在没有头痛的情况下使用。

2

正如其他人所说的,在这种情况下,您应该使用std :: string而不是char *,尽管如果这是真正需要的指针作为关键字原则上没有任何错误。

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

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

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

0

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

原来是因为Plabo指出,指针(每个编译单元都有自己的常量char *)在另一个cpp文件中访问时并不相同。