zoukankan      html  css  js  c++  java
  • STL unordered_map 自定义键值类型

    问题分析

    对于unordered_map而言,当我们插入<key, value>的时候,需要哈希函数的函数对象对key进行hash,又要利用等比函数的函数对象确保插入的键值对没有重复。然而,当我们自定义类型时,c++标准库并没有对应的哈希函数和等比函数的函数对象。因此需要分别对它们进行定义。

    STL unordered_map 官方定义

    template<class Key,
        class Ty,
        class Hash = std::hash<Key>,
        class Pred = std::equal_to<Key>,
        class Alloc = std::allocator<std::pair<const Key, Ty> > >
        class unordered_map;
        > class unordered_map
    
    • 第一个参数 key 值
    • 第二个参数 mapped value
    • 第三个参数 存储哈希函数的函数对象,并利用函数对象中的哈希函数返回类型为size_t的唯一哈希值
    • 第四个参数 等比函数的函数对象。它内部通过等比操作符’=='来判断两个key是否相等,返回值为bool类型

    重载方法

    • 构建函数实例
    #include <iostream>
    #include <unordered_map>
    #include <string>
    #include <functional>
    
    using namespace std;
    
    class Person{
    public:
    	string name;
    	int age;
    
    	Person(string n, int a){
    		name = n;
    		age = a;
    	}
    	bool operator==(const Person & p) const 
    	{
    	   return name == p.name && age == p.age;
    	}
    };
    
    size_t person_hash( const Person & p ) 
    {
        return hash<string>()(p.name) ^ hash<int>()(p.age);
    }
    
    int main(int argc, char* argv[])
    {
        //ERRO: unordered_map<Person,int,decltype(&person_hash)> ids;
        //ERRO: unordered_map<Person,int,person_hash> ids(100, person_hash );
        unordered_map<Person, int, decltype(&person_hash)> ids(100, person_hash);  ////需要把person_hash传入构造函数
        // C++11新增的关键字decltype。它可以直接获取自定义哈希函数的类型,并把它作为参数传送。
        ids[Person("Mark", 17)] = 40561;
        ids[Person("Andrew",16)] = 40562;
        for ( auto ii = ids.begin() ; ii != ids.end() ; ii++ )
            cout << ii->first.name 
            << " " << ii->first.age 
            << " : " << ii->second 
            << endl;
            return 0;
    }
    
    • 重载operator() 类
      利用重载operator()的类,将哈希函数打包成可以直接调用的类
    #include <iostream>
    #include <string>
    #include <unordered_map>
    #include <functional>
    using namespace std;
    
    class Person{
    public:
        string name;
        int age;
    
        Person(string n, int a){
            name = n;
            age = a;
        }
    
        bool operator==(const Person & p) const 
        {
            return name == p.name && age == p.age;
        }
    };
    
    struct hash_name{
    	size_t operator()(const Person & p) const{
    		return hash<string>()(p.name) ^ hash<int>()(p.age);
    	}
    };
    
    int main(int argc, char* argv[]){
        unordered_map<Person, int, hash_name> ids; //不需要把哈希函数传入构造器
        ids[Person("Mark", 17)] = 40561;
        ids[Person("Andrew",16)] = 40562;
        for ( auto ii = ids.begin() ; ii != ids.end() ; ii++ )
            cout << ii->first.name 
            << " " << ii->first.age
            << " : " << ii->second
            << endl;
        return 0;
    }
    
    • 模板定制
    #include <iostream>
    #include <unordered_map>
    #include <string>
    #include <functional>
    using namespace std;
    
    typedef pair<string,string> Name;
    
    namespace std {
        template <> //function-template-specialization
            class hash<Name>{
            public :
                size_t operator()(const Name &name ) const
                {
                    return hash<string>()(name.first) ^ hash<string>()(name.second);
                }
        };
    };
    
    int main(int argc, char* argv[])
    {
        unordered_map<Name,int> ids;
        ids[Name("Mark", "Nelson")] = 40561;
        ids[Name("Andrew","Binstock")] = 40562;
        for ( auto ii = ids.begin() ; ii != ids.end() ; ii++ )
            cout << ii->first.first 
                 << " " << ii->first.second 
                 << " : " << ii->second
                 << endl;
    	return 0;
    }
    

    当我们将模板订制包含在定义类的头文件中时,其他人无需额外工作,就可以直接用我们的类作为任何无序容器的键。这对于要使用我们自定义类的人来说,绝对是最方便的。如果你想要在多个地方用这个类,方法3是最好的选择。当然,你要确保自己的hash function不会影响std空间里的其他类。

    #include <iostream>
    #include <string>
    #include <unordered_map>
    #include <functional>
    using namespace std;
    
    class Person{
    public:
        string name;
        int age;
    
        Person(string n, int a){
            name = n;
            age = a;
        }
    };
    
    namespace std{
        template<>
        struct hash<Person>{//哈希的模板定制
        public:
            size_t operator()(const Person &p) const 
            {
                return hash<string>()(p.name) ^ hash<int>()(p.age);
            }
            
        };
        
        template<>
        struct equal_to<Person>{//等比的模板定制
        public:
            bool operator()(const Person &p1, const Person &p2) const
            {
                return p1.name == p2.name && p1.age == p2.age;
            }
            
        };
    }
    
    int main(int argc, char* argv[]){
        unordered_map<Person, int> ids;
        ids[Person("Mark", 17)] = 40561;
        ids[Person("Andrew",16)] = 40562;
        for ( auto ii = ids.begin() ; ii != ids.end() ; ii++ )
            cout << ii->first.name 
            << " " << ii->first.age
            << " : " << ii->second
            << endl;
        return 0;
    }
    
  • 相关阅读:
    非系统服务如何随系统启动时自动启动(rc.local加了可执行权限,仍然没有生效)
    centos7安装部署mysql5.7服务器
    centos7配置openldap服务器
    MySQL之高可用MHA部署
    root用户登录mysql后新建用户提示1045错误
    centos7上部署vnc服务器并实现远程桌面
    xenserver开启虚拟机时提示找不到存储介质,强制关闭和重启都没用
    固态硬盘和机械硬盘双硬盘安装win10,提示无法找到系统
    centos7安装的mysql无法启动(mysql daemon failed to start)
    实现在同一界面打开putty终端连接工具
  • 原文地址:https://www.cnblogs.com/wsl-hitsz/p/14335878.html
Copyright © 2011-2022 走看看