C++感知复制插入到std::映射中
C++ Aware duplication insert into a std::map
我有一个关于在C++中向std::映射中插入一些内容的问题。
这是我的代码:
字符串.hh:
unsigned long hashSDBM(char *strToHash){
unsigned char* str = new unsigned char[strlen(strToHash) + 1];
strncpy( (char *) str, strToHash, strlen(strToHash) );
unsigned long hash = 0;
int c;
while ((c = *str++)){
hash = c + (hash <<6) + (hash <<16) - hash;
}
return hash;
}
hashmap.hh
#include "stringutils.hh"
namespace{
using namespace std;
class MapElement{
private:
char* filename;
char* path;
public:
MapElement(char* f, char* p):filename(f), path(p){}
~MapElement(){
delete [] filename;
delete [] path;
}
char* getFileName(){ return filename; }
char* getPath(){ return path; }
};
class HashMap{
private:
map<long*, MapElement*> *hm;
long hash(char* key);
public:
HashMap(){
hm = new map<long*, MapElement*>();
}
~HashMap(){
delete hm;
}
long put(char* k, MapElement *v);
};
long HashMap::hash(char* key){
return stringutils::hashSDBM(key);
}
long HashMap::put(char* k, MapElement *v){
long *key = new long();
*key = hash(k);
pair<map<long*,MapElement*>::iterator, bool> ret;
ret = hm->insert(std::pair<long*, MapElement*>(key, v));
if(ret.second == false){
cerr<<"Already exists: "<<ret.first->second->getFileName()<<endl;
return *key;
}
cerr<<"INSERTED "<<*key<<endl;
return 0;
}
main.cc:
HashMap *hm = new HashMap();
int main(void){
MapElement *m1;
char a[] = "hello";
char b[] = "world";
m1 = new MapElement(a,b);
hm->put(a, m1);
char c[] = "thats";
char d[] = "a test";
m1 = new MapElement(c,d);
hm->put(c, m1);
char e[] = "hello";
char f[] = "test";
m1 = new MapElement(e,f);
hm->put(e, m1);
return 0;
}
它的编译去掉了任何错误或警告,当我启动它时,会生成以下输出:
插入7416051667693574450
插入8269306963433084652
插入7416051667693574450
为什么第二个插入的"你好"没有任何效果?
std::map
中的键是唯一的。如果要允许重复密钥,请使用std::multimap
。您正在使用的map::insert返回一对迭代器和一个bool
。bool表示插入是否已经实际插入(而不是键是否已经插入)。
为什么第二次插入键没有任何效果?
键是一个指针,指向具有相同值的不同long
对象的两个指针是不同的键。如果不过度使用指针,你会对自己有很大帮助。C++不是Java。
好。。。在继续之前,请阅读一本好的C++书,C++标签描述中推荐了一些好的书。
所以,这里的问题是您的代码使用指针。。。处处指针的行为并不像你想象的那样。许多语言(如Java)都有普遍的引用类型:一切都只是引用。C++不是这样一种语言,它在指针/引用和值之间有很大的区别。
在您的特定情况下,long*
是指向long
的指针。就map
而言,两个不同的指针只是:不同的,无论它们指向什么值。
所以。。。我们需要去掉那些指针。处处并且停止在C++中使用C习语。
字符串.hh
unsigned long hashSDBM(std::string const& strToHash){
unsigned long hash = 0;
for (char c: strToHash) {
hash = c + (hash <<6) + (hash <<16) - hash;
}
return hash;
}
简而言之:
- 不要在C++中使用原始
char*
,内存所有权不明确,这会导致泄漏/悬挂指针 - 适当使用
const
,一个不修改其参数的函数应该引用const
- 使用C++11进行样式循环,它们与手动代码一样高效,同时更容易阅读,更难搞砸
hashmap.hh
namespace HashMap {
class MapElement{
public:
MapElement(std::string f, std::string p):
filename(f), path(p) {}
std::string const& getFileName() const { return filename; }
std::string const& getPath() const { return path; }
private:
std::string filename;
std::string path;
};
让我们从这里开始:
- 头中没有匿名名称空间,它不会做你认为它会做的事情(读取它们)
- 没有原始指针
- 不要在商务课上浪费资源
- const正确性很重要
- 首先展示公共API,这是用户感兴趣的
继续:
class HashMap{
public:
unsigned long put(std::string const& k, MapElement v);
private:
static unsigned long hash(std::string const& key);
std::map<unsigned long, MapElement> hm;
};
inline unsigned long HashMap::hash(std::string const& key){
return stringutils::hashSDBM(key);
}
inline unsigned long HashMap::put(std::string const& k, MapElement v){
unsigned long const key = hash(k);
auto const ret = hm.emplace(key, v);
if (ret.second == false){
std:: cerr << "Already exists: " << ret.first->second.getFileName() << "n";
return key;
}
std::cerr << "INSERTED " << key << "n";
return 0;
}
好吧。。。
- 不需要这么多指针,没有它们代码会更简单
- 内部
hash
函数不访问任何状态,使其成为static
- 在可能的最后时刻声明变量,并立即初始化它们。。。它允许您使用
auto
,而不是显式命名过于复杂的类型 std::endl
不会做你认为它会做的事情(提示:它会刷新缓冲区!可能的I/O操作最慢!),只需使用普通的"n"
进一步说明:
- 如果密钥必须是文件名,为什么要让用户提交密钥?您可以从
MapElement
对象中读取。。。或者在发生冲突时打印key
(而不是文件名),以防它们不同 - 哈希不是唯一的,如果两个不同的文件名哈希到相同的数字,你会拒绝第二个。。。您应该使用一个复合密钥(hash+filename)
- 插入时返回
0
,不插入时返回key
。。。但没有什么能阻止密钥是CCD_ 20,因此接收到CCD_
main.cpp
int main(void){
HashMap::HashMap hm;
hm.put("hello", MapElement("hello", "world"));
hm.put("thats", MapElement("thats", "a test"));
hm.put("hello", MapElement("hello", "test"));
return 0;
}
最后:
- 避免全局
- 无需命名所有临时人员
相关文章:
- 使用std::函数映射对象方法
- 使用"std::unordereded_map"映射到"std::list"对象
- C++匿名结构作为std::映射值
- 如何从存储在std::映射中的std::集中删除元素
- 从嵌套在std::映射中的std::列表中删除元素的最佳方式
- C++ STD 函数运算符:有没有一种方法可以通过函数将一个向量映射到另一个向量上?
- 为什么在 std::map 上移动无法将元素从一个映射移动到另一个映射
- 当键值是 std 向量时,为什么使用 at in C++ 访问映射值如此缓慢?
- tao_idl -Gstl 不映射 std::string
- <char> 使用 Vulkan 映射内存时如何使用 std::vector 而不是 void**?
- 附加使用 Struct 作为"multikey"并将 std::vector 用作映射值的映射
- 我可以将新的 std::tuple 放入内存映射区域,并在以后读回吗?
- 如何初始化 std::向量的映射?
- 如何将不同的函数签名映射到同一个 std::map?
- 从匿名命名空间映射的辅助值抛出 std::bad_alloc
- 将唯一指针插入std::映射
- C++ODB数据库映射程序:无法在关系中使用std::weak_ptr
- 如何在C++中迭代集合映射(std::map<std::set< char>, int >)?
- std::int 和 struct 内存不足的映射 (std::Bad_alloc) c++
- 编译器不推导模板参数(映射std::vector->std::vector)