c++中std::hash以及万能hash的使用方式

 更新时间:2023年08月07日 10:05:07   作者:米安r  
这篇文章主要介绍了c++中std::hash以及万能hash的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教

c++ std::hash以及万能hash的使用

首先是标准库中的std::hash函数,对于内置的类型,标准库中是已经提供了的(包括std::string),但是若是自己自定义的类型想要求其哈希值的话,就需要自己定义其哈希值的求值方式。

下面是简单示范

#include <map>
#include <unordered_map>
#include <unordered_set>
#include <iostream>
using std::cout;
using std::endl;
class MyClass 
{
public:
    MyClass():str("hello"), data(0) {}
    bool operator==(const MyClass& rhs) const{return (data == rhs.data) && (str == rhs.str); }  //注意要重载这个==,
                                                                                                //因为unordered_set或者unordered_map
                                                                                                //中需要对元素是否相同进行判断
public: //
    int data;
    std::string str;
};
//注意这里是将自己写的偏特化也同样加入到std中,因为他的模板是在std里面的,
//具体形式可以自己简单查看一下源码中的实现形式
//然后照着写一个自己的版本就行了。
namespace std	
{
    template<>
    struct hash<MyClass>: public __hash_base<size_t, MyClass>   //标准库中有这个继承,查看一下其实只是继承两个typedef而已,
                                                                //所以不写这个继承在这个例子中也是可以运行的
                                                                //但为了更好的使用这个hash,写上去会比较好
    {
        size_t operator()(const MyClass& rhs) const noexcept    //这个const noexpect一定要写上去
        {
            return (std::hash<int>()(rhs.data)) ^ (std::hash<std::string>()(rhs.str) << 1); //当然,可以使用其他的方式来组合这个哈希值,
                                                                                            //这里是cppreference里面的例子,产生的数够乱就行。
        }
    };
}
int main()
{
    MyClass c;
    std::hash<MyClass> myHash;  //创建一个函数对象
    std::cout << myHash(c) << std::endl;
	//注意这第三个参数是typename _Hash = hash < _Value >, 是可写可不写的,因为他是有默认形式的,写出来就是这样
    std::unordered_map<MyClass, char, std::hash<MyClass>> m;	//这第三个参数
    std::unordered_set<MyClass> s;	//和上面的是一个意思,第二个参数是typename _Hash = hash < _Value >,可写可不写, 这里我是没写的。
    s.insert(c);
    s.insert(c);
    std::cin.get();
}

另外一种方式是使用侯捷老师在讲的“万能哈希函数”原理只要明白可变模板参数的使用方法就不会太难,下面是他课上使用的代码

#include <string>
using std::string;
class Customer
{
public:
    string mFirstName;
    string mLastName;
    string mAge;
    Customer(string firstName, string lastName, string age):mFirstName(firstName),mLastName(lastName),mAge(age){}
    bool operator ==(const Customer& c) const
    {
        return (mFirstName == c.mFirstName && mLastName == c.mLastName && mAge == c.mAge);
    }
};
class CustomerHash
{
public:
    std::size_t operator()(const Customer& c) const
    {
        return hash_val(c.mFirstName, c.mLastName, c.mAge);
    }
    template <typename... Types>
    size_t hash_val(const Types&... args)const
    {
        size_t seed = 0;
        hash_value(seed, args...);
        return seed;
    }
    template <typename T, typename... Types>
    void hash_value(size_t& seed,
                         const T& firstArg,
                         const Types&... args) const
    {
        hash_combine(seed, firstArg);
        hash_value(seed, args...);
    }
    template <typename T>
    void hash_value(size_t& seed,
                         const T& val) const
    {
        hash_combine(seed, val);
    }
    template<typename T>
    void hash_combine(size_t& seed,
                             const T& val) const
    {
        seed ^= std::hash<T>()(val) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
    }
};
int main()
{
    std::unordered_multiset<Customer, CustomerHash> set;
}

c++11中std::hash用法

哈希的定义

Hash,一般翻译做散列、杂凑,或音译为哈希,是把任意长度的输入(又叫做预映射pre-image)通过散列算法变换成固定长度的输出,该输出就是散列值。

这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可能会散列成相同的输出,所以不可能从散列值来确定唯一的输入值。

简单的说就是一种将任意长度的消息压缩到某一固定长度的消息摘要的函数。

哈希简介

  • Hash算法可以将一个数据转换为一个标志,这个标志和源数据的每一个字节都有十分紧密的关系。
  • Hash算法还具有一个特点,就是很难找到逆向规律。基本不可能从结果推算出输入,所以又称为不可逆的算法
  • Hash算法是一个广义的算法,也可以认为是一种思想,使用Hash算法可以提高存储空间的利用率,可以提高数据的查询效率,也可以做数字签名来保障数据传递的安全性。所以Hash算法被广泛地应用在互联网应用中。
  • Hash算法也被称为散列算法,Hash算法虽然被称为算法,但实际上它更像是一种思想。Hash算法没有一个固定的公式,只要符合散列思想的算法都可以被称为是Hash算法

常见的哈希算法

  • MD4
  • MD5
  • SHA-1及其他

哈希的用途

  • 文件校验
  • 数字签名
  • 鉴权协议

std::hash的测试示例

对于内置的类型,C++标准库中已经提供了std::hash函数计算哈希值,但如果是自己自定义的类型想求哈希值的话,则需要自己定义哈希值的求值方式。

#include <iostream>
#include <functional>
#include <string>
#include <iomanip>
int main()
{
	std::string strInput = "Peaceful in the present world, quiet in the years";
	std::hash<std::string> szHash;
	size_t hashVal = szHash(strInput);
	std::cout << std::quoted(strInput) << "'s hash=" << hashVal << "\n";
	//
	char buffer1[] = "Everything is fine";
	char buffer2[] = "Everything is fine";
	std::string strBuffer1(buffer1);
	std::string strBuffer2(buffer2);
	std::hash<char*> ptrHash;
	std::hash<std::string> strHash;
	//C++14引入std::quoted用于给字符串添加双引号
	std::cout << std::quoted(buffer1) << "'s hash<char*>=" << ptrHash(buffer1) << std::endl;
	std::cout << std::quoted(buffer2) << "'s hash<char*>=" << ptrHash(buffer2) << std::endl;
	std::cout << std::quoted(strBuffer1) << "'s hash<std::string>=" << strHash(strBuffer1) << std::endl;
	std::cout << std::quoted(strBuffer2) << "'s hash<std::string>=" << strHash(strBuffer2) << std::endl;
	//boolalpha是使bool型变量按照false、true的格式输出,如不使用该标识符,则会按照1、0的格式输出
	std::cout << "same hashes:\n" << std::boolalpha;
	std::cout << "buffer1 and buffer2: " << (ptrHash(buffer1) == ptrHash(buffer2)) << '\n';//false
	std::cout << "strBuffer1 and strBuffer2: " << (strHash(strBuffer1) == strHash(strBuffer2)) << '\n';//true
	return 0;
}

输出结果:

"Peaceful in the present world, quiet in the years"'s hash=1772844349
"Everything is fine"'s hash<char*>=3806338771
"Everything is fine"'s hash<char*>=1493957927
"Everything is fine"'s hash<std::string>=1559491232
"Everything is fine"'s hash<std::string>=1559491232
same hashes:
buffer1 and buffer2: false
strBuffer1 and strBuffer2: true

C++ 官方提供的 demo

链接地址:https://en.cppreference.com/w/cpp/utility/hash

#include <iostream>
#include <iomanip>
#include <functional>
#include <string>
#include <unordered_set>
struct S {
    std::string first_name;
    std::string last_name;
};
bool operator==(const S& lhs, const S& rhs) {
    return lhs.first_name == rhs.first_name && lhs.last_name == rhs.last_name;
}
// custom hash can be a standalone function object:
struct MyHash
{
    std::size_t operator()(S const& s) const noexcept
    {
        std::size_t h1 = std::hash<std::string>{}(s.first_name);
        std::size_t h2 = std::hash<std::string>{}(s.last_name);
        return h1 ^ (h2 << 1); // or use boost::hash_combine
    }
};
// custom specialization of std::hash can be injected in namespace std
template<>
struct std::hash<S>
{
    std::size_t operator()(S const& s) const noexcept
    {
        std::size_t h1 = std::hash<std::string>{}(s.first_name);
        std::size_t h2 = std::hash<std::string>{}(s.last_name);
        return h1 ^ (h2 << 1); // or use boost::hash_combine
    }
};
int main()
{
    std::string str = "Meet the new boss...";
    std::size_t str_hash = std::hash<std::string>{}(str);
    std::cout << "hash(" << std::quoted(str) << ") = " << str_hash << '\n';
    S obj = { "Hubert", "Farnsworth" };
    // using the standalone function object
    std::cout << "hash(" << std::quoted(obj.first_name) << ", "
              << std::quoted(obj.last_name) << ") = "
              << MyHash{}(obj) << " (using MyHash)\n" << std::setw(31) << "or "
              << std::hash<S>{}(obj) << " (using injected std::hash<S> specialization)\n";
    // custom hash makes it possible to use custom types in unordered containers
    // The example will use the injected std::hash<S> specialization above,
    // to use MyHash instead, pass it as a second template argument
    std::unordered_set<S> names = {obj, {"Bender", "Rodriguez"}, {"Turanga", "Leela"} };
    for(auto& s: names)
        std::cout << std::quoted(s.first_name) << ' ' << std::quoted(s.last_name) << '\n';
}

输出结果:

hash("Meet the new boss...") = 1861821886482076440
hash("Hubert", "Farnsworth") = 17622465712001802105 (using MyHash)
                            or 17622465712001802105 (using injected std::hash<S> specialization) 
"Turanga" "Leela"
"Bender" "Rodriguez"
"Hubert" "Farnsworth"

总结

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • C语言实现会员计费系统

    C语言实现会员计费系统

    这篇文章主要为大家详细介绍了C语言实现会员计费系统,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • C++通过TerminateProess结束进程实例

    C++通过TerminateProess结束进程实例

    这篇文章主要介绍了C++通过TerminateProess结束进程实例,是Windows应用程序设计中非常实用的技巧,需要的朋友可以参考下
    2014-10-10
  • 基于C语言实现简易扫雷游戏

    基于C语言实现简易扫雷游戏

    这篇文章主要为大家详细介绍了基于C语言实现简易扫雷游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下<BR>
    2022-01-01
  • C++二分法在数组中查找关键字的方法

    C++二分法在数组中查找关键字的方法

    这篇文章主要介绍了C++二分法在数组中查找关键字的方法,涉及C++数组查找算法的相关技巧,具有一定参考借鉴价值,需要的朋友可以参考下
    2015-09-09
  • C语言实现窗口抖动

    C语言实现窗口抖动

    这篇文章主要为大家详细介绍了C语言实现窗口抖动,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-11-11
  • OpenCV 图像拼接和图像融合的实现

    OpenCV 图像拼接和图像融合的实现

    本文主要介绍了OpenCV 图像拼接和图像融合,文中通过示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-08-08
  • C/C++实现全排列算法的示例代码

    C/C++实现全排列算法的示例代码

    本文主要介绍了C/C++实现全排列算法的示例代码,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2024-06-06
  • 应用程序操作NorFlash示例代码分享(norflash接口使用方法)

    应用程序操作NorFlash示例代码分享(norflash接口使用方法)

    相对于操作NandFlash,操作NorFlash相对简单,因为基本不需要考虑坏块,NorFlash也没有OOB区域,也跟ECC没有关系。读写擦除相对容易,下面看个例子吧
    2013-12-12
  • C语言中的putchar函数示例

    C语言中的putchar函数示例

    putchar函数函数几乎是在整个C语言中最没有知名度的,它常常被程序员冷漠地对待,这篇文章主要介绍了C语言中的putchar函数,需要的朋友可以参考下
    2022-12-12
  • C++中的explicit关键字实例浅析

    C++中的explicit关键字实例浅析

    在C++程序中很少有人去使用explicit关键字,不可否认,在平时的实践中确实很少能用的上,再说C++的功能强大,往往一个问题可以利用好几种C++特性去解决。接下来给大家介绍 C++中的explicit关键字,需要的朋友可以参考下
    2017-03-03

最新评论