首页 > 编程语言 >基于hash_table对STL unordered系列容器的封装 #C++

基于hash_table对STL unordered系列容器的封装 #C++

时间:2023-10-03 10:31:41浏览次数:50  
标签:hash iterator STL C++ Key table unordered const

概述

本文对hash_table进行封装,以模仿SGI STL对unordered系列容器进行简单实现,旨在加深对C++封装与泛型技法的体会与理解。阅读本文之前,建议先对哈希表进行学习。

unordered_map

与map一样,unordered_map的所有元素类型都是pair,pair的第一个成员为Key,第二个成员为Value。因为Key在任何情况下都不允许被修改,所以Key在pair中其实是被const修饰的。使用unordered_map的目的是根据键值快速搜寻元素,unordered_map底层是散列表,不具有对元素自动排序的功能。总体上来说,unordered_map的期望搜索效率相比map更高。从使用方面来讲,unordered_map与map的用法完全相同。

template<class Key, class Value>
class unordered_map
{
  
  /*…………*/
  
private:
  hash_table<Key, pair<const Key, Value>, MapKeyOfT> _hash_table;
};

MapKeyOfT

MapKeyOfT仿函数的设计目的与在map中的一样,均是为了拿到元素中的Key值,只不过在这里拿到Key值的目的是以Key计算元素的索引,将元素放到合适的桶中。

struct MapKeyOfT
{
  const Key& operator()(const pair<const Key, Value>& kv)
  {
    return kv.first;
  }
};

在hash_table层面,MapKeyOfT被视为模板参数KeyOfT,以同时兼容unordered_map和unordered_set,而不必关心hash_table的具体使用者及具体函数的内部实现细节。

迭代器

对于unordered_map,可以通过迭代器修改元素的Value,但不允许通过迭代器修改元素的Key,这是因为任意修改Key会破坏hash_table的组织。unordered_map的迭代器直接对hash_table进行复用即可。

public:
  typedef typename hash_table<Key, pair<const Key, Value>, MapKeyOfT>::iterator iterator;
  typedef typename hash_table<Key, pair<const Key, Value>, MapKeyOfT>::const_iterator const_iterator;

  iterator begin()
  {
    return _hash_table.begin();
  }

  iterator end()
  {
    return _hash_table.end();
  }

  const_iterator begin() const
  {
    return _hash_table.begin();
  }

  const_iterator end() const
  {
    return _hash_table.end();
  }

在对unordered_map进行插入操作时,不存在迭代器失效问题;进行删除操作时,被删除的元素的迭代器失效。

元素操作

unordered_map的大部分接口,直接对hash_table的接口进行复用即可。

//插入元素
pair<iterator, bool> insert(const pair<Key, Value>& kv)
{
  return _hash_table.insert(std::make_pair(kv.first, kv.second));
}
//删除元素
iterator erase(const Key& key)
{
  return _hash_table.erase(key);
}
//查找元素
iterator find(const Key& key) const
{
  return _hash_table.find(key);
}

operator[]是unordered_map特有的接口,本质是对hash_table的insert接口的复用,支持用户方便地通过Key对对应元素的Value进行修改。

Value& operator[](const Key& key)
{
  pair<iterator, bool> retInsert = insert(make_pair(key, Value()));
  return retInsert.first->second;
}

const Value& operator[](const Key& key) const
{
  pair<iterator, bool> retInsert = insert(make_pair(key, Value()));
  return retInsert.first->second;
}

unordered_set

unordered_set的元素不像unordered_map那样同时拥有Key和Value,可以认为,unordered_set的Key就是Value,Value就是Key。

template<class Key>
class unordered_set
{
  
  /*…………*/
  
private:
  hash_table<Key, Key, SetKeyOfT> _hash_table;
};

SetKeyOfT

对于unordered_set的KeyOfT仿函数,只需要直接返回元素的Key值即可。unordered_set的KeyOfT的设计目的完全是为了迁就unordered_map,以进行兼容。

struct SetKeyOfT
{
  const Key& operator()(const Key& key)
  {
    return key;
  }
};

迭代器

同理,不能通过unordered_set的迭代器修改键值,为了杜绝修改操作,可以直接将hash_table的const迭代器作为unordered_set的普通迭代器。unordered_set的迭代器是一种const iterators

typedef typename hash_table<Key, Key, SetKeyOfT>::const_iterator iterator;
typedef typename hash_table<Key, Key, SetKeyOfT>::const_iterator const_iterator;

iterator begin() const
{
  return _hash_table.begin();
}

iterator end() const
{
  return _hash_table.end();
}

在对unordered_set进行插入操作时,不存在迭代器失效问题;进行删除操作时,被删除的元素的迭代器失效。

元素操作

unordered_set的大部分接口可以直接复用hash_table的接口。

iterator erase(const Key& key)
{
  return _hash_table.erase(key);
}

iterator find(const Key& key) const
{
  typename HashBucket::hash_table<Key, Key, SetKeyOfT>::iterator ret = _hash_table.find(key);
  return iterator(ret);
}

对于insert接口,与set同理,由于返回值类型不一致,需要进行一层转换,支持转换的关键是,在__hash_table_iterator中额外搞一个构造函数。

template<class Key, class T, class Ref, class Ptr, class KeyOfT, class HashFunc>
struct __hash_table_iterator
{
private:
	typedef __hash_table_iterator<Key, T, T&, T*, KeyOfT, HashFunc> iterator;

public:
  //对于iterator,这个函数是拷贝构造
  //对于const_iterator,这个函数是构造
  //使用iterator构造const_iterator
  __hash_table_iterator(Node* node, const HashTable* hash_table)
    :_node(node), 
  _hash_table(hash_table)
  { }
  
  /*…………*/
};

完成这个迭代器的构造函数之后,就可以对hash_table::insert的返回值进行转化,与unordered_set::insert进行匹配。

pair<iterator, bool> insert(const Key& key)
{
  pair<typename HashBucket::hash_table<Key, Key, SetKeyOfT>::iterator, bool> ret =
    _hash_table.insert(key);
  return pair<iterator, bool>(ret.first, ret.second);
}

标签:hash,iterator,STL,C++,Key,table,unordered,const
From: https://blog.51cto.com/158SHI/7690319

相关文章

  • C++ 对拍详解 和解读
    对拍是什么#​对拍,是一个比较实用的工具。它能够非常方便地对于两个程序的输出文件进行比较,可以帮助我们实现一些自动化的比较输出结果的问题。​众所周知,几乎每一道编程题目,都会有某种正解能拿到满分;当我们想不出正解时,我们往往可以打暴力代码来获取部分分数。​但是,当我们觉......
  • C++类内存分布+ Studio工具
    书上类继承相关章节到这里就结束了,这里不妨说下C++内存分布结构,我们来看看编译器是怎么处理类成员内存分布的,特别是在继承、虚函数存在的情况下。工欲善其事,必先利其器,我们先用好VisualStudio工具,像下面这样一步一步来:  先选择左侧的C/C++->命令行,然后在其他选项这里写上......
  • C++ STL快速入门方法
    在数月之前的机试中第一次体验到STL的威力,因为自己本来一直在用C语言做开发,很多数据结构都是自己造的,比如链表、队列等,第一次接触C++STL后发现这些数据结构都已经给我提供好了,我直接拿去调用就好了,真是超级方便。最近的项目中也遇到了STL一些容器,所以现在自己好好总结一下STL中......
  • C++模板元编程(C++ template metaprogramming)
    实验平台:Win7,VS2013Community,GCC4.8.3(在线版) 所谓元编程就是编写直接生成或操纵程序的程序,C++模板给C++语言提供了元编程的能力,模板使C++编程变得异常灵活,能实现很多高级动态语言才有的特性(语法上可能比较丑陋,一些历史原因见下文)。普通用户对C++模板的使用可能不是很......
  • C++ STL 一般总结
    以下内容来源网上经过整合而成一、一般介绍     STL(StandardTemplateLibrary),即标准模板库,是一个具有工业强度的,高效的C++程序库。它被容纳于C++标准程序库(C++StandardLibrary)中,是ANSI/ISOC++标准中最新的也是极具革命性的一部分。该库包含了诸多在计算机科学领域里......
  • 十四天学会C++之第一天(入门和基本语法)
    C++的起源和历史C++诞生于20世纪80年代初,它的创造者是计算机科学家BjarneStroustrup。当时,Stroustrup在贝尔实验室工作,他希望为C语言添加一些功能,以便更好地支持系统开发。这个愿望促使他创建了C++。C++的名字来源于它的基因,其中的"C"代表了C语言,而"++"表示C语言的一个增强版本。......
  • 初识c++
    C++之父-本贾尼·斯特劳斯特卢普示例代码#include<iostream>//C++标准输入输出流的头文件等同于C语言stdio.husingnamespacestd;//为了减少命名冲突intmain(intargc,charconst*argv[]){ cout<<"helloworldc++"<<endl; return0;}作用域控制符......
  • CSES.1141 C++题解
    题意传送门有一个长度为\(n\)的歌单,问最长多少首歌互不相同?每首歌用一个\(1-10^9\)的整数表示。样例输入812132742样例输出5算法双指针算法。桶思想。对于歌单中重复出现的数,可以用桶来存储。定义两个指针i,j,i指向大数,j指向小数。当出现某个桶的数大于1时,则......
  • 【C++】函数重载 ③ ( 为函数指针赋值重载函数 )
    文章目录一、函数指针回顾1、函数指针概念2、函数指针语法3、代码示例-函数指针示例二、为函数指针赋值重载函数1、为函数指针赋值重载函数2、代码示例-为函数指针赋值重载函数博客总结:重载函数:使用相同的函数名,定义不同的函数参数列表;判定标准:只有函数......
  • 使用 C++11 原子类型 `std::atomic_flag` 实现的自旋锁
    使用C++11原子类型std::atomic_flag实现的自旋锁:#include<atomic>classSpinlock{public:Spinlock():flag(ATOMIC_FLAG_INIT){}voidlock(){while(flag.test_and_set(std::memory_order_acquire));}voidunlock(){flag.cl......