以char*为键的C++无序映射
C++ unordered_map with char* as key
尝试使用以char*
为密钥的容器unordered_map
时,我感到筋疲力尽(在Windows上,我使用的是VS 2010)。我知道我必须为char*
定义自己的比较函数,它继承自binary_function
。以下是一个示例程序。
#include<unordered_map>
#include <iostream>
#include <string>
using namespace std;
template <class _Tp>
struct my_equal_to : public binary_function<_Tp, _Tp, bool>
{
bool operator()(const _Tp& __x, const _Tp& __y) const
{ return strcmp( __x, __y ) == 0; }
};
typedef unordered_map<char*, unsigned int, ::std::tr1::hash<char*>, my_equal_to<char*> > my_unordered_map;
//typedef unordered_map<string, unsigned int > my_unordered_map;
my_unordered_map location_map;
int main(){
char a[10] = "ab";
location_map.insert(my_unordered_map::value_type(a, 10));
char b[10] = "abc";
location_map.insert(my_unordered_map::value_type(b, 20));
char c[10] = "abc";
location_map.insert(my_unordered_map::value_type(c, 20));
printf("map size: %dn", location_map.size());
my_unordered_map::iterator it;
if ((it = location_map.find("abc")) != location_map.end())
{
printf("found!n");
}
return 0;
}
我插入相同的C字符串abc
两次并查找它。第二次插入应该失败,并且在无序映射中只有一个abc
。但是,输出大小为3。这里的compare函数似乎不能正常工作。
此外,我得到了另一个关于find
函数的奇怪结果,通过多次运行程序,发现结果甚至发生了变化!有时会找到字符串abc
,而其他时候则找不到abc
!
有人能帮我吗?非常感谢你的帮助!
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
编辑:我自己为char*
定义了一个散列函数后,程序正常工作。下面列出了完整的程序代码。谢谢大家。
#include<unordered_map>
#include <iostream>
using namespace std;
template <class _Tp>
struct my_equal_to : public binary_function<_Tp, _Tp, bool>
{
bool operator()(const _Tp& __x, const _Tp& __y) const
{ return strcmp( __x, __y ) == 0; }
};
struct Hash_Func{
//BKDR hash algorithm
int operator()(char * str)const
{
int seed = 131;//31 131 1313 13131131313 etc//
int hash = 0;
while(*str)
{
hash = (hash * seed) + (*str);
str ++;
}
return hash & (0x7FFFFFFF);
}
};
typedef unordered_map<char*, unsigned int, Hash_Func, my_equal_to<char*> > my_unordered_map;
int main(){
my_unordered_map location_map;
char a[10] = "ab";
location_map.insert(my_unordered_map::value_type(a, 10));
char b[10] = "abc";
location_map.insert(my_unordered_map::value_type(b, 20));
char c[10] = "abc";
location_map.insert(my_unordered_map::value_type(c, 20));
printf("map size: %dn", location_map.size());
my_unordered_map::iterator it;
if ((it = location_map.find("abc")) != location_map.end())
{
printf("found!n");
}
return 0;
}
注意:使用char
*作为undered_map或其他STL容器的密钥类型可能是危险的,一种安全的方法(似乎是唯一的方法)是:在主函数中,new
或malloc
在堆上创建一个块(例如c字符串数组),并用c字符串填充。将这些c字符串插入到无序映射中。分配的内存块在主功能结束时被释放(通过delete
或free
)
您的比较器很好(尽管传递nullptr是未定义的,可能应该处理)
散列::std::tr1::hash<char*>
对指针进行散列,因此每个"abc"(通常)都进入不同的桶
您需要编写自己的散列函数,以保证散列("abc")总是给出相同的答案
目前,性能将很糟糕,但有一个返回0的散列,您应该会看到第二个"abc"与第一个匹配
根据注释-使用std::string
简化了内存管理,并提供了库支持的哈希和比较器,因此只有std::unordered_map<std::string, X>
可以工作。这也意味着在删除unordered map
时,所有字符串都将为您解除分配。您甚至可以安全地从堆栈上的char数组实例化std::strings
。
如果你仍然想使用char *
,那么你仍然需要自己的比较器和哈希,但你可以使用std::shared_ptr
为你管理内存(不要使用堆栈实例-做一个new char[]
)然后您将获得std::unordered_map<shared_ptr<char *>, X>
,但以后不会因内存泄漏而出现并发症。
如果你仍然想使用char *
,你就走在了正确的轨道上,但重要的是,你要使用像purify或valgrind这样的内存泄漏工具,以确保你真正控制住所有的内存管理。(这对任何项目来说都是个好主意)
最后,应避免使用全局变量。
使用如上所述的字符指针作为键几乎肯定不是您想要做的。
STL容器处理存储的值,在std::unordered_map<char *, unsigned int, ...>
的情况下,您处理的是指向c字符串的指针,这些指针甚至可能在随后的插入/删除检查中都不存在。
请注意,您的my_unordered_map
是一个全局变量,但您正试图插入局部字符数组a、b和c。当插入的c字符串超出范围时,您希望比较函数my_equal_to()
到strcmp()
是什么?(你突然有了指向随机垃圾的密钥,可以将其与新插入的未来值进行比较。)
STL映射键必须是可复制的值,不能因外部程序行为而改变其含义几乎可以肯定的是,您应该使用std::string
或类似的密钥值,即使乍一看它们的构造似乎很浪费
以下操作将完全按照您的意愿进行,并且更加安全:
#include <unordered_map>
#include <iostream>
#include <string>
using namespace std;
// STL containers use copy semantics, so don't use pointers for keys!!
typedef unordered_map<std::string, unsigned int> my_unordered_map;
my_unordered_map location_map;
int main() {
char a[10] = "ab";
location_map.insert(my_unordered_map::value_type(a, 10));
char b[10] = "abc";
location_map.insert(my_unordered_map::value_type(b, 20));
char c[10] = "abc";
location_map.insert(my_unordered_map::value_type(c, 20));
cout << "map size: " << location_map.size() << endl;
my_unordered_map::iterator it;
if ((it = location_map.find("abc")) != location_map.end()) {
cout << "found "" << it->first << "": " << it->second << endl;
}
return 0;
}
(现代C++的答案,对于仍在这个问题上磕磕碰碰的人来说)
现在,如果您使用C++17或更高版本,您可以使用std::string_view作为无序映射中的键。
std::string_view只保留对原始char*数据的引用,而不是复制它,这样,当您确定原始char*的数据超过了unordered_map时,就可以避免复制。
然而,与char*不同的是,std::string_view实现了各种方法和运算符,比如std::hash,这使得它在更多地方都很有用。
std::unordered_map<std::string_view, unsigned int> my_map;
my_map["some literal"] = 123;
printf("%dn", my_map["some literal"]);
在上面的代码中,我只在映射中放入字符串文字,这是安全的。在使用string_view键将其他东西放入地图时要小心——你有责任确保它们不会在地图之前被破坏!
当您定义诸如"abc"之类的东西时,它会被分配一个const char*。每次在程序中写入"abc"时,都会有一个新的内存被分配。因此:
const char* x = "abc";
const char* y = "abc";
return x==y;
将始终返回false,因为每次写入"abc"时都会分配新内存(如果我听起来有点重复,很抱歉)。
- 递归无序映射
- c++找不到具有相同哈希的无序集合元素
- 正在将无序映射设置为无序映射的值
- 智能指针作为无序映射键,并通过引用进行比较
- 如何使用set实现无序数据结构?
- 如何写向量的无序向量集,即unordered_set<向量<向量<int>>集合?
- 如何禁用 CPU 的无序执行
- 从C++无序集合中高效提取元素
- 打印无序映射的第二个元素,即集合
- 当我将其插入无序地图时,矢量的容量为 0
- 代码块中无序多集的编译错误17.12
- 由并发无序映射查找线程调用的函数是否安全?
- 如何按值对无序哈希映射进行排序
- gtest 期望无序元素与自定义比较器/匹配器一起使用
- 将大数字(10-12 位数字)存储在无序映射中<字符串,整数>
- 使用无序映射在STL中存储键值对
- 为C++中的无序映射获取给定输入键的错误值
- 如何将一个单词拆分成字母,并将它们放入一个无序的列表/集合中
- 将大型对象存储在无序映射中是否效率低下
- 仅从无序集合中删除一个项目