0

因此,我正在尝试为我正在尝试学习 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 和一个空行。我的问题是为什么会发生这种情况以及如何防止它发生?它是否与内存在删除时没有被“清空”有关,但它只是免费供其他程序使用,然后我没有正确初始化它?另外,如果您有时间,请指出代码中的任何错误或做得不好的地方。

如果有任何兴趣,我使用 GCC Debian 4.4.5-8 编译并使用 g++ -g file.cpp -o file

感谢您的帮助!

4

2 回答 2

3

您看到的行为是正常的:如果您get在哈希中输入了一个值,它将由您的main. 给您带来惊人结果的是您的哈希函数:

return (long int) dat % 26;

这散列了dat 指针,而不是T指向dat的。尝试:

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) 并执行 (b) - 我很惊讶你的编译器接受了你所拥有的。如果你使用 (a) 你不需要析构函数中的任何东西。如果你使用 (b),你需要delete []在你的析构函数中。

传递一个T*in get 但一个Tin set 也有点奇怪。

于 2011-12-25T13:38:21.307 回答
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;
}

请注意,您需要使用 c++0x 或 c++11 功能进行编译才能使用 std::array 类。数组类的主要好处之一是内存分配比普通的 c 样式数组更安全。

现在你可能想重新考虑设计的一些元素。尤其令人困惑的是addget具有不同的类型。这也不是人们在听到 hashmap 时通常会想到的,这种结构更像是一个集合。

同样作为编码标准说明,如果您为成员变量添加前缀,那么也用于this->访问它们有点重复。

于 2011-12-25T14:00:07.867 回答