2011-12-25 4 views
0

私は私がC++を学ぼうとしている小さなプロジェクトのために、私のニーズに合ったハッシュマップを作成しようとしています。C++、メモリおよび配列。私自身のハッシュマップを作成して運動します。予期しないデータがメモリに残っていますか?

template<class T> 
class HashMap 
{ 
public: 
    HashMap(); 
    virtual ~HashMap(); 
    void add(T value); 
    T get(T *value); 
private: 
    int hash(T *data); 
    T _hashes[26]; //I want a fixed size here 
}; 

template<class T> 
HashMap<T>::HashMap() 
{ 
    for(int i = 0; i < 26; i++) 
    this->_hashes[i] = T(); 
} 

template<class T> 
HashMap<T>::~HashMap() 
{ 
    //Don't really have anything to delete here? 
} 

template<class T> 
int HashMap<T>::hash(T *dat) 
{ 
    //Super simple, just to try things out 
    return (long int) dat % 26; 
} 

template<class T> 
T HashMap<T>::get(T *val) 
{ 
    int idx = this->hash(val); 
    cout << idx << endl; 
    //Probably somewhere here i get my problem 
    if(this->_hashes[idx]) 
    return this->_hashes[idx]; 
    return T(); 
} 

template<class T> 
void HashMap<T>::add(T val) 
{ 
    //Should probably do some check if there's already an element here. 
    this->_hashes[this->hash(&val)] = val; 
} 

た問題イムは、私は私のmain.cppには、このような何かをするとき、これは罰金コンパイルが、ということである:

HashMap<char> a = HashMap<char>(); 
a.add('h'); 
a.add('c'); 
a.add('g'); 
char *b = new char {'c'}; 
cout << a.get(b) << endl; 
delete b; 

それは通常、IDを返し、つまり私は、次のコードを持っています:

4 

と空の行だけが空の文字です。 (関数の出力は、get()メソッドである)、時にはそれが私にこのような何かが表示されます:

18 
g 

の代わりに、18と空行を。私の質問は、なぜこれが起こり、どのように私はそれを防ぐことができますか?それは削除されても、他のプログラムが自由に取れるようにしてから、それを正しく初期化しないと、メモリが「ヌル」にならないようにするものですか? また、時間がある場合は、間違いを指摘してください。そうでないと、コード内のことを行うことができません。

それがg ++ -g file.cpp -o任意の助けのために

感謝を提出し、それをコンパイルし、コンパイルするGCCのDebian 4.4.5-8を使用して、任意の関心イムのなら!

+0

'T _hashes [26]'の代わりに 'std :: array < T , 26> _hashes'を使う方がいいでしょう。また、デストラクタで '_hashes'を削除する必要があります – shuttle87

+0

なぜgetメソッドでT()を返していますか?申し訳ありませんが私はここで非常に明白な何かを理解していない場合。 – Shadow

+0

@ shuttle87私はこの標準クラスについて聞いたこともありませんでした。いくつかのドキュメントへのリンクがありますか?ヒントをありがとう!今すぐ見つけて、それを読んでください! – lfxgroove

答えて

1

は++の実装Cより慣用的です:

#include <array> 
#include <iostream> 
#define MAGIC_NUMBER 26 //rename this to something else preferably 

template<class T> 
class HashMap 
{ 
    public: 
     HashMap(); 
     virtual ~HashMap(){}; 
     void add(T value); 
     T get(T *value);//potentially confusing that add and get take different types 
    private: 
     int hash(T *data); 
     std::array<T, MAGIC_NUMBER> _hashes; //I want a fixed size here 
}; 

template<class T> 
HashMap<T>::HashMap() 
{ 
    std::fill(_hashes.begin(),_hashes.end(), T()); 
} 

template<class T> 
int HashMap<T>::hash(T *dat) 
{ 
    //Super simple, just to try things out 
    return (static_cast<int>(*dat)) % MAGIC_NUMBER;//prefer using c++ casts 
} 

template<class T> 
T HashMap<T>::get(T *val) //this is strange, you pass in a pointer and get a non-pointer 
{ 
    int idx = this->hash(val); 
    std::cout << idx << std::endl; 
    if(this->_hashes[idx]) 
    return this->_hashes[idx]; 
    return T(); 
} 

template<class T> 
void HashMap<T>::add(T val) 
{ 
    //Should probably do some check if there's already an element here. 
    this->_hashes[this->hash(&val)] = val; 
} 

int main(void){ 
    HashMap<char> a = HashMap<char>(); 
    a.add('h'); 
    a.add('c'); 
    a.add('g'); 
    char *b = new char {'c'}; 
    std::cout << a.get(b) << std::endl; 
    delete b; 
} 

あなたがのstd :: Arrayクラスの使用方法を取得するには、C++ 0xのか、C++ 11個の機能を使用してコンパイルする必要があります注意してください。配列クラスの主な利点の1つは、単なるcスタイルの配列よりもメモリ割り当ての安全性が向上することです。

今では、デザインのいくつかの要素を再検討したいことがあります。特に、addgetには異なるタイプが混乱しています。また、これは人々がハッシュマップを聞いたときに一般的に考えるものではなく、この構造はもっとセットに似ています。

また、コーディング標準の注意事項として、メンバー変数の前に接頭辞を付ける場合は、それにアクセスするためにthis->を使用することも少し賢明です。

+0

私はそれを代わりにセットに名前を変更します!それをもっと明確にするためにgetメソッドでポインタを返すべきですか?また、私は追加の原因でポインタを取るべきではなく、要素は後で誰かによって削除することができますか? – lfxgroove

+0

getメソッドに関しては、実装を見ていなくても動作するように直感的ではないので、コメント/文書化が適切であることを確認してください。 メモリ管理が心配な人は、実装の実際のポインタを隠すことができます。一般的に言えば、慣用的なC++をコーディングする場合は、できるだけRAIIや他のメモリ管理を使うのが良いでしょう。あなたが本当に削除されるコンテナの内容について心配しているのであれば、あなたはshared_ptrを調べたいかもしれません。 – shuttle87

3

表示される動作は正常です:getあなたのハッシュに入れた値は、mainで表示されます。どのようなあなたに驚くべき結果を与えていることは、あなたのハッシュ関数である:

return (long int) dat % 26; 

これはdatポインタ、ないTdatそのポイントへのハッシュ。試してみてください:

return *dat % 26; 

あなたのコードのもう一つの問題(それとも標準std::setを使用しています。):

T _hashes[26]; //I want a fixed size here (a 

this->_hashes = new T[26];     (b 

は互換性がありません。固定配列(a)を使用するか、それを割り当てる必要はありません(b)、またはプレーンポインタ(T *_hashes)とdo(b)を使用してください - あなたのコンパイラがあなたのものを受け入れるのは驚きです。 (a)を使用する場合、デストラクタには何も必要ありません。 (b)を使用する場合は、デストラクタでdelete []が必要です。

getでT*を渡しても、Tがセットに入るのはちょっと変です。ここで

+0

2番目のビット私は、私の悪い編集を忘れてしまった。今修正しました、ありがとう!私は64ビットマシンでは、私は長いintを必要とdatポインタをハッシュするので、ちょうどフォローアップの質問、そうでなければ正常なintはsufficedだろうか?また、どのように 'std :: set'をハッシュ関数で使うのですか? – lfxgroove

+2

あなたは**ポインタをハッシュしないでください。あなたは 'T'をハッシュしなければなりません(そうしないと構造は完全に無駄になります)。あなたはモジュロのみを使用するので、モジュロが定義されている 'T 'に制限されています - 私が置いた(int)キャストはおそらく私がそれについて考えて今は不要です。ハッシュ関数で 'std :: set'を使用しない場合は、ハッシュテーブルではない' HashTable'を置き換えるために使用します。 – Mat

+0

私はそれを理解しましたが、私は64ビットマシンを使用しているため、長いintですか?ああ、大丈夫。私はそれを読んでみましょう、ありがとう! – lfxgroove

関連する問題