我们可以在C ++中的类中定义哈希码方法

时间:2016-02-19 18:42:22

标签: c++ oop hashcode

我正在尝试用C ++实现一个类,我希望每个类都有自己的hashcode实现(基本上用它作为unordered_map& unordered_set中的一个键)

例如:

class CustomClass{
    int a;
    vector<int> b;
    string c;

    bool operator ==(const CustomClass& o) const{
        return ((a == o.a) && (b == o.b) && (c == o.c));
    }

    /* 
    Is it possible to define the hashcode function here instead of defining it outside the class. 
    size_t operator()() const {
        // Some custom logic for calculating hash of CustomClass using 
        // the hash Values of its individual fields

        std::size_t h = 0;
        for(int& t : b){
            h = (h ^ std::hash<int>()(t)) << 1;
        }
        return (h^(std::hash<int>()(a) << 1))^( std::hash<string>()(c) << 1);
    }
    */
};

现在,假设我想在像

这样的unordered_map中使用它
int main(){
    unoredered_map<CustomClass, int> m;
}

我有两个选择,

i)使用模板专业化

在std命名空间中注入哈希码
namespace std {
  template <> struct hash<CustomClass> {
    size_t operator()(const CustomClass& o) const {
        // Some custom logic for calculating hash of CustomClass using 
        // the hash Values of its individual fields

        size_t h = 0;
        for(int& t : o.b){
            h = (h ^ std::hash<int>()(t)) << 1;
        }
        return (h^(std::hash<int>()(o.a) << 1))^( std::hash<string>()(o.c) << 1);
    }
  };
}

OR

ii。)在实例化时每次创建unordered_map(或unordered_set)时指定此函数,即

struct HashSpecialer {
  std::size_t operator()(const CustomClass& o) const {
      std::size_t h = 0;
      for(int& t : o.b){
         h = (h ^ std::hash<int>()(t)) << 1;
      }
      return (h^(std::hash<int>()(o.a) << 1))^( std::hash<string>()(o.c) << 1);
  }
};

并且在实例化unordered_map时,我提供了这个结构。

int main(){
    unoredered_map<CustomClass, int, HashSpecialer> m;
}

我找到了两种方法,混淆使用(i)污染std命名空间和(ii)通过记住每次实例化unordered_map

时提供HashSpecializer使其变得困难

是否有可能在类定义本身中提供哈希码函数,正如我在上面的代码片段中的注释部分所述

<小时/> 注意:在java中,我们可以覆盖类中的hashCode()方法,我们可以实现此功能。一旦我覆盖了hashCode()方法,我就不用担心它了。

public class CustomClass {
    int a;
    List<Integer> b;
    String c;

    // I Let my IDE generate these methods :D
    @Override public boolean equals(Object o)
    {
        if (this == o)
            return true;
        if (o == null || getClass() != o.getClass())
            return false;

        CustomClass that = (CustomClass) o;

        if (a != that.a)
            return false;
        if (b != null ? !b.equals(that.b) : that.b != null)
            return false;
        return c != null ? c.equals(that.c) : that.c == null;

    }

    // This one too :D
    @Override public int hashCode()
    {
        int result = a;
        result = 31 * result + (b != null ? b.hashCode() : 0);
        result = 31 * result + (c != null ? c.hashCode() : 0);
        return result;
    }
}

我正在寻找类似的东西,因为这证明非常方便。

3 个答案:

答案 0 :(得分:5)

我认为你的问题的解决方案是调整你对美学上令人愉悦的C ++程序的理解。

std::hash的专业化不会污染std名称空间,相反,您应该考虑std::hash是用于控制unordered_map如何与您的班级合作的自定义点。

这样的特化是类的接口的一部分(并且可以是类的朋友),其方式与operator +()之类的二元运算符应该是非成员函数完全相同,并且仍然是其中的一部分界面。

答案 1 :(得分:1)

  

如果在多个命名空间中定义了CustomClass,我不知道如何处理这种情况

这就是命名空间的用途。你的问题解决了:)

namespace std {
  template <> struct hash<NameSpace1::CustomClass> { ... };
  template <> struct hash<NameSpace2::CustomClass> { ... };
}

C ++不是Java。忘记你知道的任何Java,它会对你有很大的帮助。许多Java习语在C ++中看起来非常不同,或者在C ++中简单无用或不适用。

答案 2 :(得分:1)

C ++的设计一目了然可能更加冗长和繁琐,但它实际上更灵活。所有类都不只是 来了解哈希码;并且不同的容器可以要求不同的哈希约束而不会侵入所包含的类型。

这就是专业化的好处。您可以将代码挂钩到标准库中,而不会实际影响它。毕竟,你专注于自己的类型,所以它不是侵入性的。

因此,为您的类型专门化std::hash()是完全可以的,只要您打算将其用作合理的默认值即可。否则,您可能需要考虑std::unordered_map和您的密钥类型组合的别名模板。