封装unordered系列容器

一、前言

前面我们已经模拟实现和封装了map和set这两个容器,今天我们来模拟封装一下unordered系列的两个容器,我们前面已经对unordered系列的两个容器有所了解,它们的底层使用的是哈希桶来实现的,前面的文章已经讲过哈希桶的实现了。下面我们和实现map和set两个容器的方法一样依然分步骤进行实现。

二、基本架构的实现

unordered_map的基本架构

namespace Code_Journey
{
	struct MapKeyOfT
	{
		template<class K, class V>
		const K& operator()(const pair<K, V>& kv) const
		{
			return kv.first;
		}
	};

	template<class K, class V, class Hash = HashFunc<K>>
	class unordered_map
	{
		typedef HashTable<K, pair<K, V>, MapKeyOfT, Hash> ht;
	public:
		bool insert(const pair<K, V>& kv)
		{
			return _ht.insert(kv);
		}
	private:
		ht _ht;
	};
}

unordered_set的基本架构

namespace Code_Journey
{
	struct SetKeyOfT
	{
		template<class K>
		const K& operator()(const K& key) const
		{
			return key;
		}
	};

	template<class K, class Hash = HashFunc<K>>
	class unordered_set
	{
		typedef HashTable<K, K, SetKeyOfT, Hash> ht;
	public:
		bool insert(const K& key)
		{
			return _ht.insert(key);
		}
	private:
		ht _ht;
	};
}

三、实现普通迭代器

这里实现普通迭代器要注意下面几个点:

第一点:两个类相互依赖的问题,因为实现迭代器要用到HashTable这个类,所以造成了两个类相互依赖的问题,如果想要解决这个问题就需要提前在迭代器实现前声明一下HashTable这个类,告诉编译器我已经实现了这个类。

第二点:operator++的实现逻辑,实现operator++的主要在于我们要分清楚他的几种情况,第一种情况就是当前的哈希桶还没有遍历完,这种情况最简单,只需要我们将_node更新一下即可,第二种情况就是这个哈希桶遍历完成,需要遍历下一个哈希桶,这种情况需要借助HashTable这个类,然后找到下一个桶的第一个位置直接更新给_node就可以了,这里需要注意的一点是一定要从下一个桶开始找起,否则就会陷入无限循环,就是将hashi++之后在找。进入无限循环的原因就是当hashi这个桶内如果只有它自己一个元素就会陷入无线打印该元素。因为我们计算出的hashi这个地址一定是不为空的,也就是说每次调用这个++都会打印这个hashi这个位置的值。第三种是最简单的就是接下来是空,并且后续没有桶了,这种情况直接给_node赋值为nullptr即可。

第三点:设置友元类,由于我们实现的operator++内使用了HashTable的私有成员,所以我们要将我们的迭代器设置成HashTable的友元类,记住将模板设计成友元的时候要带全模板参数。

// 声明HashTable模板
template<class K, class T, class KeyOfT, class Hash>
class HashTable;

template<class K, class T, class KeyOfT, class Hash>
struct HashTabel_Iterator
{
	typedef HashTable<K, T, KeyOfT, Hash> HT;
	typedef HashTabel_Iterator<K, T, KeyOfT, Hash> Self;
	typedef HashTableNode<T> Node;
	Node* _node;
	HT* _ht;

	HashTabel_Iterator(Node* node, HT* ht)
		:_node(node)
		,_ht(ht)
	{}

	T& operator*()
	{
		return _node->_data;
	}
	T* operator->()
	{
		return &_node->_data;
	}
	bool operator==(const Self& it) const
	{
		return _node == it._node;
	}
	bool operator!=(const Self& it) const
	{
		return _node != it._node;
	}
	Self& operator++()
	{
		Hash hs;
		KeyOfT kot;
		if (_node->_next)
		{
			_node = _node->_next;
			return *this;
		}
		size_t hashi = hs(kot(_node->_data)) % _ht->_table.size();
		// 这里一定要注意先对hashi进行++操作,否则会进行无限循环
		// 因为hashi这个位置一定是cur存在的位置
		hashi++;
		for (size_t i = hashi; i < _ht->_table.size(); i++)
		{
			Node* cur = _ht->_table[i];
			if (cur)
			{
				_node = cur;
				return *this;
			}
		}
		_node = nullptr;
		return *this;
	}
};

四、实现const迭代器

在实现const迭代器的时候很有可能会遇到需要乱七八糟的错误,出现这些错误的原因多半都是出自模板的报错。之前也说过模板的报错一报就是一长串,这种报错会很让人崩溃,这也是为什么我封装东西总是一步一步的来,这样可以极大的减少报错的概率,就算出现了许多乱七八糟的错误也知道是那一步出现的错误,时刻要记住满就是快,一步一步的来,不可能一口吃成胖子,当代码能力成熟了再尝试去跳步。

下面来说一下封装const迭代器时遇到的错误:

  • 迭代器成员_ht必须定义成const类型,因为在实现const迭代器的时候this指针是经过const修饰的,如果这里不加const就无法构建const迭代器。
  • 封装const迭代器的时候会增加模板参数,一定要按照顺序一步一步的去增加,否则就会报出乱七八糟的错误。
  • 一定要记住普通对象只能调用普通的迭代器,只有const对象才能调用const迭代器。
template<class K, class T, class KeyOfT, class Hash, class Ref, class Ptr>
struct HashTable_Iterator
{
	typedef HashTable<K, T, KeyOfT, Hash> HT;
	typedef HashTable_Iterator<K, T, KeyOfT, Hash, Ref, Ptr> Self;
	typedef HashTableNode<T> Node;
	Node* _node;
	const HT* _ht;

	HashTable_Iterator(Node* node, const HT* ht)
		:_node(node)
		,_ht(ht)
	{}

	Ref operator*()
	{
		return _node->_data;
	}
	Ptr operator->()
	{
		return &_node->_data;
	}
	bool operator==(const Self& it) const
	{
		return _node == it._node;
	}
	bool operator!=(const Self& it) const
	{
		return _node != it._node;
	}
	Self& operator++()
	{
		Hash hs;
		KeyOfT kot;
		if (_node->_next)
		{
			_node = _node->_next;
			return *this;
		}
		size_t hashi = hs(kot(_node->_data)) % _ht->_table.size();
		// 这里一定要注意先对hashi进行++操作,否则会进行无限循环
		// 因为hashi这个位置一定是cur存在的位置
		hashi++;
		for (size_t i = hashi; i < _ht->_table.size(); i++)
		{
			Node* cur = _ht->_table[i];
			if (cur)
			{
				_node = cur;
				return *this;
			}
		}
		_node = nullptr;
		return *this;
	}
};

五、重载[]运算符

下面我们来实现一下unordered_map的operator[]。重载[]依然是借助insert成员函数,insert在重载[]时起到的时查找和插入的功能。所以我们要先将insert成员函数的返回值改成pair<iterator,bool>然后我们用它返回的迭代器取结点中的pair成员的second成员。

V& operator[](const K& key)
{
	pair<iterator, bool> ret = _ht.insert({key, V()});
	return ret.first->second;
}

六、封装后的代码

unordered_map封装后的代码

#pragma once
#include"HashTable.h"

namespace Code_Journey
{
	struct MapKeyOfT
	{
		template<class K, class V>
		const K& operator()(const pair<K, V>& kv) const
		{
			return kv.first;
		}
	};

	template<class K, class V, class Hash = HashFunc<K>>
	class unordered_map
	{
		typedef HashTable<K, pair<K, V>, MapKeyOfT, Hash> ht;
	public:
		typedef typename HashTable<K, pair<K, V>, MapKeyOfT, Hash>::Iterator iterator;
		typedef typename HashTable<K, pair<K, V>, MapKeyOfT, Hash>::ConstIterator const_iterator;
		iterator begin()
		{
			return _ht.Begin();
		}
		iterator end()
		{
			return _ht.End();
		}
		const_iterator begin()const
		{
			return _ht.Begin();
		}
		const_iterator end()const
		{
			return _ht.End();
		}
		pair<iterator, bool> insert(const pair<K, V>& kv)
		{
			return _ht.insert(kv);
		}
		V& operator[](const K& key)
		{
			pair<iterator, bool> ret = _ht.insert({key, V()});
			return ret.first->second;
		}
		iterator find(const K& key)
		{
			return _ht.find(key);
		}
		bool erase(const K& key)
		{
			return _ht.erase(key);
		}
	private:
		ht _ht;
	};
}

unordered_set封装后的代码

#pragma once
#include"HashTable.h"

namespace Code_Journey
{
	struct SetKeyOfT
	{
		template<class K>
		const K& operator()(const K& key) const
		{
			return key;
		}
	};

	template<class K, class Hash = HashFunc<K>>
	class unordered_set
	{
		typedef HashTable<K, K, SetKeyOfT, Hash> ht;
	public:
		typedef typename HashTable<K, K, SetKeyOfT, Hash>::Iterator iterator;
		typedef typename HashTable<K, K, SetKeyOfT, Hash>::ConstIterator const_iterator;
		iterator begin()
		{
			return _ht.Begin();
		}
		iterator end()
		{
			return _ht.End();
		}

		const_iterator begin()const
		{
			return _ht.Begin();
		}
		const_iterator end()const
		{
			return _ht.End();
		}
		pair<iterator, bool> insert(const K& key)
		{
			return _ht.insert(key);
		}
		iterator find(const K& key)
		{
			return _ht.find(key);
		}
		bool erase(const K& key)
		{
			return _ht.erase(key);
		}
	private:
		ht _ht;
	};
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值