0

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

...

ハッシュマップ.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

キー「hello」からの 2 番目の挿入が効果がないのはなぜですか?

4

3 に答える 3

2

のキーstd::mapは一意です。重複キーを許可する場合は、を使用しますstd::multimap。使用しているmap::insertは、イテレータとのペアを返しますbool。boolは、挿入が実際に挿入されたかどうかを示します(キーがすでに挿入されているかどうかではありません)。

于 2012-10-01T18:27:44.673 に答える
1

キーの2回目の挿入が効果がないのはなぜですか?

キーはポインターでありlong、同じ値を持つ異なるオブジェクトへの2つのポインターは異なるキーです。ポインタを過度に使用しないことで、本当に自分を助けることができます。C++はJavaではありません。

于 2012-10-01T18:28:43.113 に答える
0

よかった...先に進む前に、良い C++ の本を読んでください。C++ タグの説明で推奨されている良い本があります。


したがって、ここでの問題は、コードがポインターを使用しているということです...どこでも...ポインターは、あなたが思っているように動作しません。Java などの多くの言語には、参照型が広く普及しています。すべてが単なる参照です。C++ はそのような言語ではありません。一方のポインター/参照と他方の値の間に大きな違いがあります。

あなたの特定のケースでlong*は、へのポインタlongです。関係する限り、2 つの異なるmapポインターは、それらが指すものの値に関係なく、まさにそれです。

だから... 私たちはそれらのポインタを取り除く必要があります。どこにでも。そして、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 を使用します。手動コードと同じくらい効率的でありながら、はるかに読みやすく、台無しにするのがはるかに困難です。

ハッシュマップ.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-rectness の問題
  • 最初にパブリック 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"代わりにplainを使用してください

その他の注意事項:

  • キーがファイル名でなければならない場合、なぜユーザーにキーを送信させるのですか? MapElement代わりにオブジェクトから読み取ることができます...またはkey、衝突が発生したときに(ファイル名ではなく)それらが異なる場合に備えて出力します
  • ハッシュは一意ではありません。2 つの異なるファイル名が同じ番号にハッシュされた場合、2 番目のものは拒否されます...複合キー (ハッシュ + ファイル名) を使用する必要があります
  • 0挿入するときは戻りますが、そうでないときは戻ります...しかし、キーを受け取ることkeyを妨げるものは何もないため、ユーザーは何が起こったのか疑問に思います00

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

そして仕上げのために:

  • グローバルを避ける
  • すべての一時的な名前を付ける必要はありません
于 2016-12-29T11:58:21.953 に答える