【C++从0到王者】第三十七站:模拟unordered_map和unordered_set

news2024/11/19 14:29:38

文章目录

  • 一、哈希表的修改
  • 二、迭代器
    • 1.普通迭代器
    • 2.const迭代器
    • 3.插入返回值
    • 4.unordered_map的operator[]
  • 三、完整代码

一、哈希表的修改

如下是我们一开始的哈希表

namespace hash_bucket
{
	template<class K,class V>
	struct HashNode
	{
		pair<K, V> _kv;
		HashNode<K, V>* _next = nullptr;
		HashNode(const pair<K, V>& kv)
			:_kv(kv)
		{}
	};

	template<class K>
	struct DefaultHashFunc
	{
		size_t operator()(const K& key)
		{
			return (size_t)key;
		}
	};
	template<>
	struct DefaultHashFunc<string>
	{
		size_t operator()(const string& str)
		{
			size_t hashi = 0;
			for (auto ch : str)
			{
				hashi = hashi * 131 + ch;
			}
			return hashi;
		}
	};

	template<class K, class V, class HashFunc = DefaultHashFunc<K>>
	class HashTable
	{
		typedef HashNode<K,V> Node;
	public:
		HashTable()
			:_n(0)
		{
			_table.resize(10, nullptr);
		}
		
		bool Insert(const pair<K, V>& kv)
		{
			if (Find(kv.first))
			{
				return false;
			}

			HashFunc hf;
			if (_n == _table.size())
			{
				size_t newSize = _table.size() * 2;
				vector<Node*> newTable(newSize, nullptr);
				for (int i = 0; i < _table.size(); i++)
				{
					Node* cur = _table[i];
					while (cur)
					{
						Node* next = cur->_next;
						size_t hashi = hf(cur->_kv.first) % newTable.size();
						
						cur->_next = newTable[hashi];
						newTable[hashi] = cur;
						cur = next;
					}
					_table[i] = nullptr;
				}

				_table.swap(newTable);
			}
			size_t hashi = hf(kv.first) % _table.size();
			Node* newnode = new Node(kv);
			newnode->_next = _table[hashi];
			_table[hashi] = newnode;
			++_n;
			return true;
		}

		void Print()
		{
			for (int i = 0; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				printf("[%d]->", i);
				while (cur)
				{
					cout << cur->_kv.first << ":" << cur->_kv.second << "->";
					cur = cur->_next;
				}
				cout << "NULL" << endl;
			}
		}

		Node* Find(const K& key)
		{
			HashFunc hf;
			size_t hashi = hf(key) % _table.size();
			Node* cur = _table[hashi];
			while (cur)
			{
				if (cur->_kv.first == key)
				{
					return cur;
				}
				cur = cur->_next;
			}
			return nullptr;
		}

		bool Erase(const K& key)
		{
			HashFunc hf;
			size_t hashi = hf(key) % _table.size();
			Node* cur = _table[hashi];
			Node* prev = nullptr;
			while (cur)
			{
				if (cur->_kv.first == key)
				{
					if (prev)
					{
						prev->_next = cur->_next;
					}
					else
					{
						_table[hashi] = cur->_next;
					}
					delete cur;
					cur = nullptr;
					_n--;
					return true;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}
			}
			return false;
		}

		~HashTable()
		{
			for (int i = 0; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				while (cur)
				{
					Node* next = cur->_next;
					delete cur;
					cur = next;
				}
				_table[i] = nullptr;
			}
		}
	private:
		vector<Node*> _table;
		size_t _n;
	};
}

现在为了将他改装位unordered系列容器,我们还需要对这个哈希表做出一些修改

对于哈希表的修改与红黑树去封装map和set是非常类似的。

首先是将结点都改为T类型的,这个T类型对于set而言是K,对于map而言是pair<K,V>

image-20231011163137468

image-20231011163148796

image-20231011163157456

然后接下来我们继续修改其他接口,我们先来修改插入接口,首先是该类型为T类型

image-20231011163250299

但是这样的话,会导致,没有kv了,而Find接口需要一个K类型的,对于这个问题,我们与红黑树的解决办法一样

image-20231011164129473

image-20231011165223977

image-20231011164208489

然后我们去修改Insert里面的问题,最终修改为如下所示

image-20231011170818276

		bool Insert(const T& data)
		{
			KeyOfT kot;
			if (Find(kot(data)))
			{
				return false;
			}

			HashFunc hf;
			if (_n == _table.size())
			{
				size_t newSize = _table.size() * 2;
				vector<Node*> newTable(newSize, nullptr);
				for (int i = 0; i < _table.size(); i++)
				{
					Node* cur = _table[i];
					while (cur)
					{
						Node* next = cur->_next;
						size_t hashi = hf(kot(cur->_data)) % newTable.size();
						
						cur->_next = newTable[hashi];
						newTable[hashi] = cur;
						cur = next;
					}
					_table[i] = nullptr;
				}

				_table.swap(newTable);
			}
			size_t hashi = hf(kot(data)) % _table.size();
			Node* newnode = new Node(data);
			newnode->_next = _table[hashi];
			_table[hashi] = newnode;
			++_n;
			return true;
		}

接下来修改Find函数,加上kot的逻辑即可

image-20231011170854778

		Node* Find(const K& key)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* cur = _table[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return cur;
				}
				cur = cur->_next;
			}
			return nullptr;
		}

然后是删除逻辑

image-20231011170935550

		bool Erase(const K& key)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* cur = _table[hashi];
			Node* prev = nullptr;
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					if (prev)
					{
						prev->_next = cur->_next;
					}
					else
					{
						_table[hashi] = cur->_next;
					}
					delete cur;
					cur = nullptr;
					_n--;
					return true;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}
			}
			return false;
		}

有了上面的改造哈希表,我们就可以先暂时的使用一下unordered_map和set

image-20231011172309055

image-20231011172318313

void test1()
{
	Sim::unordered_map<int, int> um;
	um.insert(make_pair(1, 1));
	um.insert(make_pair(5, 5));
	um.insert(make_pair(2, 2));
	um.insert(make_pair(3, 3));

	Sim::unordered_set<int> us;
	us.insert(1);
	us.insert(5);
	us.insert(3);
	us.insert(2);	
}

image-20231011172442381

二、迭代器

1.普通迭代器

对于哈希表的迭代器,我们需要注意的有以下几点

首先是最为核心的功能++,他该如何++是个麻烦,如果是再当前桶的下一个还有结点,那是最好的情况,可以直接向后指即可。但是如果当前桶已经完了,如何去找下一个桶呢?

我们的办法是这样的,再去定义一个哈希表的指针,因为有了哈希表的指针,我们就可以去访问哈希表中的容量,根据我们当前迭代器指向的数据,我们可以计算出当前在哪一个桶里面,然后我们在从下一个桶里开始,去依次寻找桶中有数据的元素即可。

image-20231011185045267

如下就是迭代器的设计

	template<class K, class T, class KeyOfT, class HashFunc>
	struct HTIterator
	{
		typedef HashNode<T> Node;
		typedef HTIterator<K, T, KeyOfT, HashFunc> Self;

		Node* _node;
		HashTable<K, T, KeyOfT, HashFunc>* _pht;
		
		HTIterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
			:_node(node)
			,_pht(pht)
		{}

		Self& operator++()
		{
			if (_node->_next)
			{
				_node = _node->_next;
			}
			else
			{
				KeyOfT kot;
				HashFunc hf;
				size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();
				++hashi;
				while (hashi < _pht->_table.size())
				{
					if (_pht->_table[hashi])
					{
						_node = _pht->_table[hashi];
						return *this;
					}
					++hashi;
				}
				_node = nullptr;
			}
			return *this;
		}
		bool operator!=(const Self& ht)
		{
			return _node != ht._node;
		}
		bool operator==(const Self& ht)
		{
			return _node == ht._node;
		}
		T& operator*()
		{
			return _node->_data;
		}
		T* operator->()
		{
			return &_node->_data;
		}
	};

不过需要特别注意的是,我们必须得在哈希表中有一个友元声明,因为迭代器中需要去通过访问哈希表的底层数组的大小。而这个数组是一个私有成员变量,所以我们需要使用友元。

image-20231011185601343

还需要注意的是,在迭代器中,我们使用了哈希表指针,在哈希表中,使用了迭代器。因为迭代器中只用到了哈希表的指针,所以需要在迭代器前面有一个哈希表的前置声明,让迭代器中的类型可以通过编译。

image-20231011185746109

如下就是map和set中的迭代器函数了

image-20231011185840291

image-20231011185849921

使用如下的测试用例

void test1()
{
	Sim::unordered_map<int, int> um;
	um.insert(make_pair(1, 1));
	um.insert(make_pair(5, 5));
	um.insert(make_pair(2, 2));
	um.insert(make_pair(3, 3));

	Sim::unordered_map<int, int>::iterator umit = um.begin();
	while (umit != um.end())
	{
		cout << umit->first << ":" << umit->second << endl;
		++umit;
	}
	cout << endl;

	Sim::unordered_map<string, string> ums;
	ums.insert(make_pair("sort", "排序"));
	ums.insert(make_pair("insert", "插入"));
	ums.insert(make_pair("delete", "删除"));
	ums.insert(make_pair("begin", "开始"));
	for (auto e : ums)
	{
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;


	Sim::unordered_set<int> us;
	us.insert(1);
	us.insert(5);
	us.insert(3);
	us.insert(2);
	
	Sim::unordered_set<int>::iterator usit = us.begin();
	while (usit != us.end())
	{
		cout << *usit << " ";
		++usit;
	}
	cout << endl;
	
}

运行结果如下所示

image-20231011190101399

2.const迭代器

看似上面的代码已经可以跑了,但是还是存在一些问题,因为我们还没有解决const迭代器的问题,这就导致了,在set中key可以被修改,在map中,key也可以被修改,这是一个很严重的问题。所以现在我们需要来解决他

我们先来处理const迭代器的问题

const的迭代器的处理思路还是和之前一样的,多传两个模板参数来解决

image-20231012174142422

image-20231012174204929

image-20231012174342889

这样一来,我们就成功的将普通迭代器和const迭代器都处理好了

如下所示,我们再将set中的迭代器给处理好,让无论是const对象还是普通对象,都只能去访问const迭代器。无论是const迭代器还是普通迭代器其实都是const迭代器

image-20231012184137787

不过在这里我们会会遇到一个新的问题

image-20231012184205643

这里的问题其实报错报的不是很明显,下面这个更详细一些

image-20231012184403541

其实这里是因为,this指针出现了权限放大的问题

因为我们set要使用的时候统统都把this指针当成了const修饰以后的this指针。而我们迭代器的构造函数中这个哈希表指针的类型是一个普通的类型,所以权限放大了

image-20231012184853107

那么我们该如何解决呢?

有两种思路,一种是直接重载然后强制类型转换

image-20231012190208927

这样的编译器也确实可以通过

还有一种方案就是直接将原来的内置成员类型改为const指针

image-20231012190325772

为什么敢将这个指针改为const呢?

因为我们这个迭代器类中,并没有通过这个指针去修改哈希表中的任何事情,我们仅仅只是利用这个哈希表指针去读取哈希表的大小以及一些结点的指针。所以我们才敢将这个指针改为const

我们在这个迭代器中一切的修改都是使用node指针去进行修改的,只有通过它的解引用才可以实现

如此一来,哈希表中的迭代器已经可以跑起来了,那么问题又来了,可不可以不要第一个构造呢?

其实是可以的,如果没有第一个构造函数,因为第二个也是可以用的,因为仅仅这里就只是权限的缩小。即便普通迭代器也是可以传过来的

前面是对于set的处理,现在来处理以下map的,对于map的就很简单了,我们直接加上一个const即可,这样的话对于普通的map对象,由于底层第一个参数是const类型的,那么即便迭代器返回了,也是无法修改的。

image-20231013003228108

3.插入返回值

解决了const迭代器的问题,接下来我们会发现我们的插入的返回值还是一个bool值,事实上应该是一个pair类型的,这一点也是为了支撑map的[]运算符重载

如下所示,我们先改变哈希表的insert

image-20231013170226018

要改变哈希表的insert,我们还需要去改变find的返回值,它应该返回一个迭代器

image-20231013170303209

不过在这里会出现跟使用红黑树封装set时一样的问题

image-20231013171331039

这里是因为unordered_set中的iterator其实是const_iterator。所以是两个完全不一样的类型。当然不可以直接进行转化了。

不过在这里我们可能会有一些疑惑,为什么下面这段代码应该也涉及到了这个问题,但是在之前的测试中没有任何问题呢?

image-20231013175425685

上面的这个问题可以等效为下面的问题

image-20231013182057332

明明是两个不一样的类型,为什么不会报错呢?

其实这就是拷贝构造函数的妙用了

在pair<K,V>类中,其实类里面重载了两个参数都为const,或者其中一个为const的情况,这里的其实就是一个利用普通对象去构造const对象的妙用。

上面的问题还可以进一步在list的代码中找到

image-20231013180233613

在这里,我们之前模拟实现过list,我们也知道,这里的迭代器其实也是一个对象,lt是一个普通的对象,它的bengin自然就是一个普通迭代器,那么第三行代码中,普通迭代器对象可以直接赋值给const迭代器对象。我们知道里面的这两个迭代器是完全不同的两个对象,至于他们为什么会支持,就是因为重载了一个拷贝构造函数,当这个迭代器为普通迭代器的时候,它其实没有什么用,就算不写它编译器也会默认生成一个拷贝构造函数。当这个迭代器为const迭代器的时候,那么这里就不是拷贝构造函数了,而是构造函数,这个构造函数是利用一个普通迭代器去构造一个const迭代器的。

image-20231013182335462

类似的场景我们在前面使用红黑树的时候我们也使用过

7df44c02619828ffc3abc3ca352141da

我们当时也是通过添加一个拷贝构造函数来完成的

所以本来模板实例化后不同的类型是不可以相互转化的,但是由于拷贝构造函数的妙用,让这个拷贝构造函数有了双重意义以后,便行得通了。使得模板参数中带有const的类型可以使用普通的进行构造。

类似与下面的代码

image-20231013184939893

好了现在回过头来,虽然举了四个例子,但是目标还是不能忘了,我们引发上面思考的原因就是因为类型不匹配问题所导致的,现在我们有了上面的如何用普通迭代器构造const迭代器的思路以后。我们能否将这个思路运用到我们的代码上呢?毕竟我们现在代码遇到的问题就是哈希表返回的是一个普通迭代器,而我们set需要返回一个const迭代器。

即现在,我们需要用这个普通迭代器构造一个const迭代器,那么我们就需要写一个具有双重意义的拷贝构造函数了

image-20231013190632327

有了在这个拷贝构造函数以后,我们在insert中这样写就可以了

image-20231013190715811

至此,我们的迭代器就终于实现了!!!

4.unordered_map的operator[]

这个有了insert的基础以后,就非常简单了

image-20231013193343346

测试也是没有任何问题的

image-20231013193405308

三、完整代码

HashTable.cpp文件

#pragma once
namespace hash_bucket
{
	template<class K>
	struct DefaultHashFunc
	{
		size_t operator()(const K& key)
		{
			return (size_t)key;
		}
	};
	template<>
	struct DefaultHashFunc<string>
	{
		size_t operator()(const string& str)
		{
			size_t hashi = 0;
			for (auto ch : str)
			{
				hashi = hashi * 131 + ch;
			}
			return hashi;
		}
	};

	template<class T>
	struct HashNode
	{
		T _data;
		HashNode<T>* _next = nullptr;
		HashNode(const T& data)
			:_data(data)
		{}
	};

	//前置声明
	template<class K, class T, class KeyOfT, class HashFunc>
	class HashTable;

	template<class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc>
	struct HTIterator
	{
		typedef HashNode<T> Node;
		typedef HTIterator<K, T, Ptr, Ref, KeyOfT, HashFunc> Self;
		typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> Iterator;


		Node* _node;
		const HashTable<K, T, KeyOfT, HashFunc>* _pht;
		
		//HTIterator(Node* node, HashTable<K, T, KeyOfT, HashFunc>* pht)
		//	:_node(node)
		//	,_pht(pht)
  //		{}
		HTIterator(Node* node, const HashTable<K, T, KeyOfT, HashFunc>* pht)
			:_node(node)
			, _pht(pht)
		{}

		HTIterator(const Iterator& it)
			:_node(it._node)
			,_pht(it._pht)
		{}

		//HTIterator(Node* node, const HashTable<K, T, KeyOfT, HashFunc>* pht)
		//	:_node(node)
		//	//, _pht(pht)
		//{
		//	_pht = (HashTable<K, T, KeyOfT, HashFunc>*)pht;
		//}

		Self& operator++()
		{
			if (_node->_next)
			{
				_node = _node->_next;
			}
			else
			{
				KeyOfT kot;
				HashFunc hf;
				size_t hashi = hf(kot(_node->_data)) % _pht->_table.size();
				++hashi;
				while (hashi < _pht->_table.size())
				{
					if (_pht->_table[hashi])
					{
						_node = _pht->_table[hashi];
						return *this;
					}
					++hashi;
				}
				_node = nullptr;
			}
			return *this;
		}
		bool operator!=(const Self& ht)
		{
			return _node != ht._node;
		}
		bool operator==(const Self& ht)
		{
			return _node == ht._node;
		}
		Ref operator*()
		{
			return _node->_data;
		}
		Ptr operator->()
		{
			return &_node->_data;
		}
	};

	template<class K, class T, class KeyOfT, class HashFunc = DefaultHashFunc<K>>
	class HashTable
	{
		typedef HashNode<T> Node;

		template<class K, class T, class Ptr, class Ref, class KeyOfT, class HashFunc>
		friend struct HTIterator;

	public:
		typedef HTIterator<K, T, T*, T&, KeyOfT, HashFunc> iterator;
		typedef HTIterator<K, T, const T*, const T&, KeyOfT, HashFunc> const_iterator;


		iterator begin()
		{
			Node* cur = nullptr;
			for (int i = 0; i < _table.size(); i++)
			{
				if (_table[i])
				{
					cur = _table[i];
					break;
				}
			}
			return iterator(cur, this);
		}
		iterator end()
		{
			return iterator(nullptr, this);
		}

		const_iterator begin() const
		{
			Node* cur = nullptr;
			for (int i = 0; i < _table.size(); i++)
			{
				if (_table[i])
				{
					cur = _table[i];
					break;
				}
			}
			return const_iterator(cur, this);
		}
		const_iterator end() const
		{
			return const_iterator(nullptr, this);
		}


		HashTable()
			:_n(0)
		{
			_table.resize(10, nullptr);
		}
		
		pair<iterator, bool> Insert(const T& data)
		{
			KeyOfT kot;
			iterator it = Find(kot(data));
			if (it != end())
			{
				return make_pair(it, false);
			}

			HashFunc hf;
			if (_n == _table.size())
			{
				size_t newSize = _table.size() * 2;
				vector<Node*> newTable(newSize, nullptr);
				for (int i = 0; i < _table.size(); i++)
				{
					Node* cur = _table[i];
					while (cur)
					{
						Node* next = cur->_next;
						size_t hashi = hf(kot(cur->_data)) % newTable.size();
						
						cur->_next = newTable[hashi];
						newTable[hashi] = cur;
						cur = next;
					}
					_table[i] = nullptr;
				}

				_table.swap(newTable);
			}
			size_t hashi = hf(kot(data)) % _table.size();
			Node* newnode = new Node(data);
			newnode->_next = _table[hashi];
			_table[hashi] = newnode;
			++_n;
			return make_pair(iterator(newnode, this), true);
		}
		iterator Find(const K& key)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* cur = _table[hashi];
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					return iterator(cur, this);
				}
				cur = cur->_next;
			}
			return iterator(nullptr, this);
		}

		bool Erase(const K& key)
		{
			HashFunc hf;
			KeyOfT kot;
			size_t hashi = hf(key) % _table.size();
			Node* cur = _table[hashi];
			Node* prev = nullptr;
			while (cur)
			{
				if (kot(cur->_data) == key)
				{
					if (prev)
					{
						prev->_next = cur->_next;
					}
					else
					{
						_table[hashi] = cur->_next;
					}
					delete cur;
					cur = nullptr;
					_n--;
					return true;
				}
				else
				{
					prev = cur;
					cur = cur->_next;
				}
			}
			return false;
		}

		~HashTable()
		{
			for (int i = 0; i < _table.size(); i++)
			{
				Node* cur = _table[i];
				while (cur)
				{
					Node* next = cur->_next;
					delete cur;
					cur = next;
				}
				_table[i] = nullptr;
			}
		}
	private:
		vector<Node*> _table;
		size_t _n;
	};
}

unordered_map.h文件

#pragma once


namespace Sim
{
	template<class K,class V>
	class unordered_map
	{
		struct MapKeyOfT
		{
			const K& operator()(const pair<K, V>& kv)
			{
				return kv.first;
			}
		};
	public:
		typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::iterator iterator;
		typedef typename hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT>::const_iterator 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(make_pair(key, V()));
			return ret.first->second;
		}

	private:
		hash_bucket::HashTable<K, pair<const K, V>, MapKeyOfT> _ht;
	};

}

unordered_set.h文件

#pragma once

namespace Sim
{
	template<class K>
	class unordered_set
	{
		struct SetKeyOfT
		{
			const K& operator()(const K& key)
			{
				return key;
			}
		};
	public:
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator iterator;
		typedef typename hash_bucket::HashTable<K, K, SetKeyOfT>::const_iterator const_iterator;


		const_iterator begin() const
		{
			return _ht.begin();
		}
		const_iterator end() const
		{
			return _ht.end();
		}

		pair<iterator, bool> insert(const K& key)
		{
			pair<typename hash_bucket::HashTable<K, K, SetKeyOfT>::iterator, bool> ret = _ht.Insert(key);
			return pair<iterator, bool>(ret.first, ret.second);
		}
	private:
		hash_bucket::HashTable<K, K, SetKeyOfT> _ht;

	};
}

test.cpp文件

#define _CRT_SECURE_NO_WARNINGS 1

#include <iostream>
#include <vector>
#include <string>
#include <list>
using namespace std;
#include "HashTable.h"
#include "my_unordered_map.h"
#include "my_unordered_set.h"



void test1()
{
	Sim::unordered_map<int, int> um;
	um.insert(make_pair(1, 1));
	um.insert(make_pair(5, 5));
	um.insert(make_pair(2, 2));
	um.insert(make_pair(3, 3));

	Sim::unordered_map<int, int>::iterator umit = um.begin();
	while (umit != um.end())
	{
		cout << umit->first << ":" << umit->second << endl;
		++umit;
	}
	cout << endl;

	Sim::unordered_map<string, string> ums;
	ums.insert(make_pair("sort", "排序"));
	ums.insert(make_pair("insert", "插入"));
	ums.insert(make_pair("delete", "删除"));
	ums.insert(make_pair("begin", "开始"));
	for (auto e : ums)
	{
		//e.first += 'x';
		//e.second += 'x';
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;
	ums["insert"] = "xxx";
	ums["proceess"] = "yyyy";
	for (auto e : ums)
	{
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;

	Sim::unordered_set<int> us;
	us.insert(1);
	us.insert(5);
	us.insert(3);
	us.insert(2);
	
	Sim::unordered_set<int>::iterator usit = us.begin();
	while (usit != us.end())
	{
		//*usit = 1;
		cout << *usit << " ";
		++usit;
	}
	cout << endl;
	
}

//void test2()
//{
//	pair<int, int> p1(1, 1);
//	pair<const int, const int> p2(p1);
//	pair<int, int> p3(p2);
//}
//void test3()
//{
//	list<int> lt;
//	list<int>::iterator it1 = lt.begin();
//	list<int>::const_iterator it2 = lt.begin();
//
//}
//
//template<class T,class Ref>
//class A
//{
//public:
//	A(Ref a)
//		:_a(a)
//	{}
//	A(const A<T, T&>& a)
//		:_a(a._a)
//	{}
//
//	int _a;
//};
//void test4()
//{
//	int x = 1;
//	A<int, int&> a(x);
//	A<int, const int&> b(a);
//}
int main()
{
	test1();
	return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1088847.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【【萌新的SOC学习之自定义IP核 AXI4接口】】

萌新的SOC学习之自定义IP核 AXI4接口 自定义IP核-AXI4接口 AXI接口时序 对于一个读数据信号 AXI突发读 不要忘记 最后还有拉高RLAST 表示信号的中止 实验任务 &#xff1a; 通过自定义一个AXI4接口的IP核 &#xff0c;通过AXI_HP接口对PS端 DDR3 进行读写测试 。 S_AXI…

RocketMQ为什么要保证订阅关系一致

这篇文章&#xff0c;笔者想聊聊 RocketMQ 最佳实践之一&#xff1a;保证订阅关系一致。 订阅关系一致指的是同一个消费者 Group ID 下所有 Consumer 实例所订阅的 Topic 、Tag 必须完全一致。 如果订阅关系不一致&#xff0c;消息消费的逻辑就会混乱&#xff0c;甚至导致消息丢…

FreeSOLO: Learning to Segment Objects without Annotations*(论文解析)

FreeSOLO: Learning to Segment Objects without Annotations* 摘要引言 摘要 实例分割是一项基本的计算机视觉任务&#xff0c;旨在识别并分割图像中的每个对象。然而&#xff0c;要学习实例分割通常需要昂贵的注释&#xff0c;例如边界框和分割掩模。在这项工作中&#xff0…

使用PyQt5创建图片查看器应用程序

使用PyQt5创建图片查看器应用程序 作者&#xff1a;安静到无声 个人主页 在本教程中&#xff0c;我们将使用PyQt5库创建一个简单的图片查看器应用程序。这个应用程序可以显示一系列图片&#xff0c;并允许用户通过按钮切换、跳转到不同的图片。 1. 准备工作 首先&#xff0…

小黑子—MyBatis:第四章

MyBatis入门4.0 十 小黑子进行MyBatis参数处理10.1 单个简单类型参数10.1.1 单个参数Long类型10.1.2 单个参数Date类型 10.2 Map参数10.3 实体类参数&#xff08;POJO参数&#xff09;10.4 多参数10.5 Param注解&#xff08;命名参数&#xff09;10.6 Param注解源码分析 十一 小…

CVE-2017-15715 apache换行解析文件上传漏洞

影响范围 httpd 2.4.0~2.4.29 复现环境 vulhub/httpd/CVE-2017-15715 docker-compose 漏洞原理 在apache2的配置文件&#xff1a; /etc/apache2/conf-available/docker-php.conf 中&#xff0c;php的文件匹配以正则形式表达 ".php$"的正则匹配模式意味着以.ph…

金蝶EAS、EAS Cloud远程代码执行漏洞

【漏洞概述】 金蝶 EAS 及 EAS Cloud 是金蝶软件公司推出的一套企业级应用软件套件&#xff0c;旨在帮助企业实现全面的管理和业务流程优化。 【漏洞介绍】 金蝶 EAS 及 EAS Cloud 存在远程代码执行漏洞 【影响版本】 金蝶 EAS 8.0&#xff0c;8.1&#xff0c;8.2&#xf…

风电光伏混合储能功率小波包分解、平抑前后波动性分析、容量配置、频谱分析、并网功率波动分析(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

流计算概述(林子雨慕课课程)

文章目录 11. 流计算概述11.1 流计算概述11.1.1 数据的处理模型11.1.2 流计算概念与典型框架 11.2 流计算处理流程11.3 流计算的应用11.4 开源流计算框架Storm11.4.1 Storm 简介11.4.2 Storm设计思想11.4.3 Storm框架设计 11.5 Spark Spark Streaming Samza以及三种流计算框架比…

Python如何17行代码画一个爱心

&#x1f308;write in front&#x1f308; &#x1f9f8;大家好&#xff0c;我是Aileen&#x1f9f8;.希望你看完之后&#xff0c;能对你有所帮助&#xff0c;不足请指正&#xff01;共同学习交流. &#x1f194;本文由Aileen_0v0&#x1f9f8; 原创 CSDN首发&#x1f412; 如…

xtrabackup全备 增备

版本针对mysql8.0版本 官方下载地址 https://www.percona.com/downloads 自行选择下载方式 yum安装方式 1、下载上传服务器 安装软件 [rootmaster mysql]# ll percona-xtrabackup-80-8.0.33-28.1.el7.x86_64.rpm -rw-r--r--. 1 root root 44541856 Oct 10 13:25 percona-x…

android 判断是否打开了蓝牙网络共享

最近做项目遇到需要判断手机是否打开了蓝牙网络共享的开关 //调用isBluetoothPanTetheringOn(context) {if (it) {Log.i("TAG","已打开")} else {Log.i("TAG","未打开")context.gotoBleShareSettings()} }/*** 是否打开蓝牙网络共享**…

idea中取消class文件显示所有方法的显示

一 idea中class文件取消显示方法 1.1 取消显示方法 1.显示如下 2.操作如下 3.显示如下

2023年中国固废处理行业研究报告

第一章 行业概况 1.1 定义 固体废物处理是一个日益重要的领域&#xff0c;随着中国城市化进程的加速和工业产值的持续增长&#xff0c;固体废物的产生量也在不断上升。根据《固体废物污染环境防治法》的定义&#xff0c;固体废物包括了人类在生产、生活和其他活动中产生的固态…

基于单目的光流法测速

目录 1.简介 2.代码实现 1.简介 基于单目的光流法是一种常见的计算机视觉技术&#xff0c;用于估计图像序列中物体的运动速度。它通过分析连续帧之间的像素变化来推断物体在图像中的移动情况。 背景&#xff1a; 光流法是计算机视觉领域中最早的运动估计方法之一&#xff0c…

BUUCTF 金三 1

BUUCTF:https://buuoj.cn/challenges 题目描述&#xff1a; 只有一个附件&#xff0c;下载下来有一张GIF图片。 解题思路&#xff1a; 本题一共有2种解法&#xff08;本人找到的&#xff09; 方法一&#xff1a; 1、打开这张GIF图片&#xff0c;观察到不正常闪动&#xff0c;似…

《YOLO医学影像检测》专栏介绍 CSDN独家改进实战

&#x1f4a1;&#x1f4a1;&#x1f4a1;YOLO医学影像检测&#xff1a;http://t.csdnimg.cn/N4zBP ✨✨✨实战医学影像检测项目&#xff0c;通过创新点验证涨点可行性&#xff1b; ✨✨✨入门医学影像检测到创新&#xff0c;不断打怪进阶&#xff1b; 1.血细胞检测介绍 数据…

数据结构 - 2(顺序表10000字详解)

一&#xff1a;List 1.1 什么是List 在集合框架中&#xff0c;List是一个接口&#xff0c;继承自Collection。 Collection也是一个接口&#xff0c;该接口中规范了后序容器中常用的一些方法&#xff0c;具体如下所示&#xff1a; Iterable也是一个接口&#xff0c;Iterabl…

security+JWT

securityJWT 添加依赖准备工作sqlUserInfoUserMapperUserService、UserServiceImpl 创建JwtUtils工具类&#xff0c;做token的生成和校验进入Security创建AccountDetailsServiceImpl&#xff0c;并且实现UserDetailsService编写登录操作 创建拦截器JWTAuthenticationFilter继承…

mac电影特效合成软件nuke15 完美激活版下载

Nuke 15是一款由英国The Foundry公司开发的专业的合成软件&#xff0c;被广泛用于电影、电视和广告制作中的后期合成和特效制作。 Mac软件下载&#xff1a;nuke15 完美激活版下载 Win软件下载&#xff1a;NUKE 13 中文激活版 Nuke 15拥有强大的功能和灵活性&#xff0c;可以帮助…