簡體   English   中英

C ++感知復制插入到std :: map中

[英]C++ Aware duplication insert into a std::map

我有一個關於在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;
  }

...

哈希圖

#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

為什么第二個插入鍵“ hello”沒有作用?

std::map中的鍵是唯一的。 如果要允許重復的鍵,請使用std::multimap 您正在使用的map :: insert返回一對迭代器和一個bool 布爾值指示插入是否已實際插入(如果密鑰已經在其中,則不會)。

為什么第二次插入鍵沒有任何作用?

您的鍵是一個指針,指向具有相同值的不同long對象的兩個指針是不同的鍵。 您可以通過不過度使用指針來真正幫助自己。 C ++不是Java。

好的...請先閱讀一本好書,然后再繼續閱讀,C ++標記說明中推薦了一些好書。


因此,這里的問題是您的代碼在所有地方都使用了指針……並且指針的行為不像您認為的那樣。 諸如Java之類的許多語言都有普遍的引用類型:所有內容都只是一個引用。 C ++不是這種語言,它一方面使指針/引用與另一方面使值產生很大差異。

在您的特定情況下, long*是指向long的指針。 map而言,兩個不同的指針僅代表它們: distant ,無論它們指向的值如何。

所以...我們需要擺脫那些指針。 到處。 並停止在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 ++中使用raw char* ,內存所有權尚不清楚,這會導致泄漏/懸空指針
  • 適當地使用const ,不修改其參數的函數應采用const引用它們
  • 使用C ++ 11進行樣式循環,它們與手動代碼一樣有效,同時更易於閱讀,更難擰緊

哈希圖

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

讓我們從這里開始:

  • 標頭中沒有匿名名稱空間,它不執行您認為的功能(在其上閱讀)
  • 沒有原始指針
  • 無需擺弄商務艙的資源
  • 常數正確性很重要
  • 首先展示公共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 ,而在插入時返回key ...但是沒有什么阻止該鍵為0因此接收到0會使用戶懷疑發生了什么

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

最后:

  • 避免全局
  • 無需命名所有臨時人員

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM