C ++意识到重复插入到std :: map中

时间:2012-10-01 18:21:54

标签: c++ hash insert duplicates stdmap

我有一个关于在C ++中向std :: map插入内容的问题。

这是我的代码:

stringutils.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;
}

它会编译出任何错误或警告,当我启动它时,以下输出是generatéd:

INSERTED 7416051667693574450

INSERTED 8269306963433084652

INSERTED 7416051667693574450

为什么第二次插入关键字“你好”没有任何影响?

3 个答案:

答案 0 :(得分:2)

std::map中的密钥是唯一的。如果您想允许重复密钥,请使用std::multimap。你正在使用的map :: insert返回一对迭代器和一个bool。 bool表示插入是否实际插入(如果密钥已经存在,则表示插入)。

答案 1 :(得分:1)

  

为什么键的第二个插入没有任何效果?

您的密钥是一个指针,指向具有相同值的不同long个对象的两个指针是不同的密钥。如果不过度使用指针,你真的会帮助自己。 C ++不是Java。

答案 2 :(得分:0)

天哪......在继续学习之前,请先阅读一本好的C ++书籍,C ++标签说明中推荐了好书。

所以,这里的问题是你的代码使用指针......无处不在......指针的行为与你认为的不同。许多语言(如Java)都具有普遍的引用类型:一切都只是一个引用。 C ++不是这样一种语言,它一方面指针/引用与另一方面的值之间存在很大差异。

在您的具体情况下,long*是指向long的指针。就map而言,两个不同的指针就是: distinct ,无论它们指向的是什么价值。

所以......我们需要摆脱那些指针。到处。并停止在C ++中使用C语言。

  

stringutils.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-correctness重要
  • 首先展示公共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(而不是文件名),以防它们不同
  • 哈希不是唯一的,如果两个不同的文件名哈希到相同的数字,你将拒绝第二个...你应该使用复合键(哈希+文件名)
  • 您在插入时会返回0,而在{... 1}}时却没有...但是没有任何内容可以阻止密钥key,因此接收0会让用户怀疑发生了什么
  

的main.cpp

0

结束:

  • 避免全局
  • 无需为所有临时工作者命名