c++中std::hash以及萬(wàn)能hash的使用方式
c++ std::hash以及萬(wàn)能hash的使用
首先是標(biāo)準(zhǔn)庫(kù)中的std::hash函數(shù),對(duì)于內(nèi)置的類型,標(biāo)準(zhǔn)庫(kù)中是已經(jīng)提供了的(包括std::string),但是若是自己自定義的類型想要求其哈希值的話,就需要自己定義其哈希值的求值方式。
下面是簡(jiǎn)單示范
#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); } //注意要重載這個(gè)==,
//因?yàn)閡nordered_set或者unordered_map
//中需要對(duì)元素是否相同進(jìn)行判斷
public: //
int data;
std::string str;
};
//注意這里是將自己寫(xiě)的偏特化也同樣加入到std中,因?yàn)樗哪0迨窃趕td里面的,
//具體形式可以自己簡(jiǎn)單查看一下源碼中的實(shí)現(xiàn)形式
//然后照著寫(xiě)一個(gè)自己的版本就行了。
namespace std
{
template<>
struct hash<MyClass>: public __hash_base<size_t, MyClass> //標(biāo)準(zhǔn)庫(kù)中有這個(gè)繼承,查看一下其實(shí)只是繼承兩個(gè)typedef而已,
//所以不寫(xiě)這個(gè)繼承在這個(gè)例子中也是可以運(yùn)行的
//但為了更好的使用這個(gè)hash,寫(xiě)上去會(huì)比較好
{
size_t operator()(const MyClass& rhs) const noexcept //這個(gè)const noexpect一定要寫(xiě)上去
{
return (std::hash<int>()(rhs.data)) ^ (std::hash<std::string>()(rhs.str) << 1); //當(dāng)然,可以使用其他的方式來(lái)組合這個(gè)哈希值,
//這里是cppreference里面的例子,產(chǎn)生的數(shù)夠亂就行。
}
};
}
int main()
{
MyClass c;
std::hash<MyClass> myHash; //創(chuàng)建一個(gè)函數(shù)對(duì)象
std::cout << myHash(c) << std::endl;
//注意這第三個(gè)參數(shù)是typename _Hash = hash < _Value >, 是可寫(xiě)可不寫(xiě)的,因?yàn)樗怯心J(rèn)形式的,寫(xiě)出來(lái)就是這樣
std::unordered_map<MyClass, char, std::hash<MyClass>> m; //這第三個(gè)參數(shù)
std::unordered_set<MyClass> s; //和上面的是一個(gè)意思,第二個(gè)參數(shù)是typename _Hash = hash < _Value >,可寫(xiě)可不寫(xiě), 這里我是沒(méi)寫(xiě)的。
s.insert(c);
s.insert(c);
std::cin.get();
}另外一種方式是使用侯捷老師在講的“萬(wàn)能哈希函數(shù)”原理只要明白可變模板參數(shù)的使用方法就不會(huì)太難,下面是他課上使用的代碼
#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,一般翻譯做散列、雜湊,或音譯為哈希,是把任意長(zhǎng)度的輸入(又叫做預(yù)映射pre-image)通過(guò)散列算法變換成固定長(zhǎng)度的輸出,該輸出就是散列值。
這種轉(zhuǎn)換是一種壓縮映射,也就是,散列值的空間通常遠(yuǎn)小于輸入的空間,不同的輸入可能會(huì)散列成相同的輸出,所以不可能從散列值來(lái)確定唯一的輸入值。
簡(jiǎn)單的說(shuō)就是一種將任意長(zhǎng)度的消息壓縮到某一固定長(zhǎng)度的消息摘要的函數(shù)。
哈希簡(jiǎn)介
- Hash算法可以將一個(gè)數(shù)據(jù)轉(zhuǎn)換為一個(gè)標(biāo)志,這個(gè)標(biāo)志和源數(shù)據(jù)的每一個(gè)字節(jié)都有十分緊密的關(guān)系。
- Hash算法還具有一個(gè)特點(diǎn),就是很難找到逆向規(guī)律。基本不可能從結(jié)果推算出輸入,所以又稱為不可逆的算法
- Hash算法是一個(gè)廣義的算法,也可以認(rèn)為是一種思想,使用Hash算法可以提高存儲(chǔ)空間的利用率,可以提高數(shù)據(jù)的查詢效率,也可以做數(shù)字簽名來(lái)保障數(shù)據(jù)傳遞的安全性。所以Hash算法被廣泛地應(yīng)用在互聯(lián)網(wǎng)應(yīng)用中。
- Hash算法也被稱為散列算法,Hash算法雖然被稱為算法,但實(shí)際上它更像是一種思想。Hash算法沒(méi)有一個(gè)固定的公式,只要符合散列思想的算法都可以被稱為是Hash算法
常見(jiàn)的哈希算法
- MD4
- MD5
- SHA-1及其他
哈希的用途
- 文件校驗(yàn)
- 數(shù)字簽名
- 鑒權(quán)協(xié)議
std::hash的測(cè)試示例
對(duì)于內(nèi)置的類型,C++標(biāo)準(zhǔn)庫(kù)中已經(jīng)提供了std::hash函數(shù)計(jì)算哈希值,但如果是自己自定義的類型想求哈希值的話,則需要自己定義哈希值的求值方式。
#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用于給字符串添加雙引號(hào)
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型變量按照f(shuō)alse、true的格式輸出,如不使用該標(biāo)識(shí)符,則會(huì)按照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;
}輸出結(jié)果:
"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';
}輸出結(jié)果:
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"
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
C++教程之進(jìn)制轉(zhuǎn)換的實(shí)現(xiàn)方法
在C++中進(jìn)行進(jìn)制轉(zhuǎn)換可以通過(guò)標(biāo)準(zhǔn)庫(kù)函數(shù)或自定義算法實(shí)現(xiàn),本文主要為大家整理了兩種常見(jiàn)場(chǎng)景的轉(zhuǎn)換方法及示例代碼,有需要的小伙伴可以根據(jù)需求進(jìn)行選擇2025-04-04
C++JSON庫(kù)CJsonObject詳解(輕量簡(jiǎn)單好用)
CJsonObject是基于cJSON全新開(kāi)發(fā)一個(gè)C++版的JSON庫(kù),CJsonObject的最大優(yōu)勢(shì)是輕量簡(jiǎn)單好用,開(kāi)發(fā)效率極高,對(duì)多層嵌套json的讀取和生成使用非常簡(jiǎn)單,喜歡的朋友一起看看吧2021-04-04
C++實(shí)現(xiàn)順序表的常用操作(插入刪出查找輸出)
實(shí)現(xiàn)順序表的插入,刪除,查找,輸出操作在C語(yǔ)言中經(jīng)常用到。下面小編給大家整理實(shí)現(xiàn)代碼,一起看下吧2016-08-08
C語(yǔ)言編程計(jì)算信噪比SNR理解學(xué)習(xí)
這篇文章主要介紹了C語(yǔ)言編程信噪比SNR計(jì)算的理解學(xué)習(xí),信噪比,英文名稱叫做SNR或S/N(SIGNAL-NOISE RATIO)。是指一個(gè)電子設(shè)備或者電子系統(tǒng)中信號(hào)與噪聲的比例2021-10-10
Qt中QAbstractButton::setAutoRepeat設(shè)置按住按鈕自動(dòng)重復(fù)
QAbstractButton::setAutoRepeat接口用于設(shè)置按鈕自動(dòng)重復(fù)功能,本文就來(lái)介紹一下Qt中QAbstractButton::setAutoRepeat設(shè)置按住按鈕自動(dòng)重復(fù),感興趣的可以了解一下2026-06-06
詳解c/c++賦值函數(shù)(重載=號(hào)運(yùn)算符)
大家都知道c++里的各種運(yùn)算符都是用函數(shù)實(shí)現(xiàn)的,比如=就等號(hào)函數(shù),所以當(dāng)用=給一個(gè)對(duì)象賦值的時(shí)候,實(shí)際調(diào)用的是=號(hào)所對(duì)應(yīng)的=號(hào)函數(shù)。下面通過(guò)本文給大家介紹c/c++賦值函數(shù)(重載=號(hào)運(yùn)算符),感興趣的朋友一起看看吧2018-08-08
C++使用宏實(shí)現(xiàn)動(dòng)態(tài)庫(kù)加載
開(kāi)發(fā)的時(shí)候,有些項(xiàng)目不能靜態(tài)鏈接動(dòng)態(tài)庫(kù),需要程序運(yùn)行時(shí)加載動(dòng)態(tài)庫(kù)。本文將使用宏來(lái)實(shí)現(xiàn)動(dòng)態(tài)庫(kù)的加載,感興趣的小伙伴可以跟隨小編一起了解一下2022-12-12

