在 std::map 中使用 char* 作为键
我试图找出为什么以下代码不起作用,并且我假设这是使用 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 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
您需要为映射提供一个比较函子,否则它将比较指针,而不是它指向的以空结尾的字符串。一般来说,只要您希望地图键成为指针,就会出现这种情况。
例如:
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:
除非您绝对 100% 确定将使用完全相同的指针而不是字符串来访问地图,否则不能使用
char*
。示例:
如果您使用
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:
If you access map with
s1
you will get a different location than accessing it withs2
.两个 C 样式字符串可以具有相同的内容,但位于不同的地址。该
map
比较的是指针,而不是内容。转换为
std::map
的成本可能没有您想象的那么多。但如果您确实需要使用 const char* 作为映射键,请尝试:
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:您可以使其与
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 addedconst
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 themconst
, 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.您正在将使用
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 achar
.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 anint
.正如其他人所说,在这种情况下,您可能应该使用 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.
当我尝试在多个源文件中查找元素时,很难使用 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.
std::map
将使用默认的std::less
来比较char*
键,这将进行指针比较。但是您可以像这样指定您自己的 Compare 类:请记住,您必须确保 char* 指针有效。
无论如何,我建议使用
std::map
。std::map<char*,int>
will use the defaultstd::less<char*,int>
to comparechar*
keys, which will do a pointer comparison. But you can specify your own Compare class like this: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.您可以绑定执行相同工作的 lambda。
You can bind a lambda that does the same job.
使用任何键类型都没有问题,只要它支持比较(
<
、>
、==
)和赋值即可。应该提到的一点 - 考虑到您正在使用模板类。结果编译器将为
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*
andint*
. 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.