【C++ STL学习笔记】C+(5),2024年最新Golang开发者该学习哪些东西提高竞争力-程序员宅基地

技术标签: 2024年程序员学习  学习  c++  笔记  

先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7

深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年最新Golang全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上Go语言开发知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

如果你需要这些资料,可以添加V获取:vip1024b (备注go)
img

正文

//执行此语句会抛出 out_of_range 异常
//cout << umap.at(“GO教程”);
return 0;
}

程序执行结果为:

http://c.biancheng.net/python/

此程序中,第 13 行代码用于获取 umap 容器中键为“Python教程”对应的值,由于 umap 容器确实有符合条件的键值对,因此可以成功执行;而第 17 行代码,由于当前 umap 容器没有存储以“Go教程”为键的键值对,因此执行此语句会抛出 out_of_range 异常。

  1. 运算符和 at() 成员方法基本能满足大多数场景的需要。除此之外,还可以借助 unordered_map 模板中提供的 find() 成员方法。

和前面方法不同的是,通过 find() 方法得到的是一个正向迭代器,该迭代器的指向分以下 2 种情况:

  1. 当 find() 方法成功找到以指定元素作为键的键值对时,其返回的迭代器就指向该键值对;
  2. 当 find() 方法查找失败时,其返回的迭代器和 end() 方法返回的迭代器一样,指向容器中最后一个键值对之后的位置。

举个例子:

#include
#include
#include <unordered_map>
using namespace std;
int main()
{
//创建 umap 容器
unordered_map<string, string> umap{
{“Python教程”,“http://c.biancheng.net/python/”},
{“Java教程”,“http://c.biancheng.net/java/”},
{“Linux教程”,“http://c.biancheng.net/linux/”} };
//查找成功
unordered_map<string, string>::iterator iter = umap.find(“Python教程”);
cout << iter->first << " " << iter->second << endl;
//查找失败
unordered_map<string, string>::iterator iter2 = umap.find(“GO教程”);
if (iter2 == umap.end()) {
cout << “当前容器中没有以"GO教程"为键的键值对”;
}
return 0;
}

程序执行结果为:

Python教程 http://c.biancheng.net/python/
当前容器中没有以"GO教程"为键的键值对

  1. 除了 find() 成员方法之外,甚至可以借助 begin()/end() 或者 cbegin()/cend(),通过遍历整个容器中的键值对来找到目标键值对。

举个例子:

#include
#include
#include <unordered_map>
using namespace std;
int main()
{
//创建 umap 容器
unordered_map<string, string> umap{
{“Python教程”,“http://c.biancheng.net/python/”},
{“Java教程”,“http://c.biancheng.net/java/”},
{“Linux教程”,“http://c.biancheng.net/linux/”} };
//遍历整个容器中存储的键值对
for (auto iter = umap.begin(); iter != umap.end(); ++iter) {
//判断当前的键值对是否就是要找的
if (!iter->first.compare(“Java教程”)) {
cout << iter->second << endl;
break;
}
}
return 0;
}

程序执行结果为:

http://c.biancheng.net/java/

以上 4 种方法中,前 2 种方法基本能满足多数场景的需要,建议初学者首选 at() 成员方法!

C++ unordered_map insert()用法精讲

为了方便用户向已建 unordered_map 容器中添加新的键值对,该容器模板中提供了 insert() 方法,本节就对此方法的用法做详细的讲解。

unordered_map 模板类中,提供了多种语法格式的 insert() 方法,根据功能的不同,可划分为以下几种用法。

  1. insert() 方法可以将 pair 类型的键值对元素添加到 unordered_map 容器中,其语法格式有 2 种:

//以普通方式传递参数
pair<iterator,bool> insert ( const value_type& val );
//以右值引用的方式传递参数
template
pair<iterator,bool> insert ( P&& val );

有关右值引用,可阅读《C++右值引用详解》一文,这里不再做具体解释。

以上 2 种格式中,参数 val 表示要添加到容器中的目标键值对元素;该方法的返回值为 pair类型值,内部包含一个 iterator 迭代器和 bool 变量:

  • 当 insert() 将 val 成功添加到容器中时,返回的迭代器指向新添加的键值对,bool 值为 True;
  • 当 insert() 添加键值对失败时,意味着当前容器中本就存储有和要添加键值对的键相等的键值对,这种情况下,返回的迭代器将指向这个导致插入操作失败的迭代器,bool 值为 False。

举个例子:

#include
#include
#include <unordered_map>
using namespace std;
int main()
{
//创建空 umap 容器
unordered_map<string, string> umap;
//构建要添加的键值对
std::pair<string, string>mypair(“STL教程”, “http://c.biancheng.net/stl/”);
//创建接收 insert() 方法返回值的pair类型变量
std::pair<unordered_map<string, string>::iterator, bool> ret;
//调用 insert() 方法的第一种语法格式
ret = umap.insert(mypair);
cout << "bool = " << ret.second << endl;
cout << “iter -> " << ret.first->first <<” " << ret.first->second << endl;

//调用 insert() 方法的第二种语法格式
ret = umap.insert(std::make_pair(“Python教程”,“http://c.biancheng.net/python/”));
cout << "bool = " << ret.second << endl;
cout << "iter -> " << ret.first->first << " " << ret.first->second << endl;
return 0;
}

程序执行结果为:

bool = 1
iter -> STL教程 http://c.biancheng.net/stl/
bool = 1
iter -> Python教程 http://c.biancheng.net/python/

从输出结果很容易看出,两次添加键值对的操作,insert() 方法返回值中的 bool 变量都为 1,表示添加成功,此时返回的迭代器指向的是添加成功的键值对。

  1. 除此之外,insert() 方法还可以指定新键值对要添加到容器中的位置,其语法格式如下:

//以普通方式传递 val 参数
iterator insert ( const_iterator hint, const value_type& val );
//以右值引用方法传递 val 参数
template
iterator insert ( const_iterator hint, P&& val );

以上 2 种语法格式中,hint 参数为迭代器,用于指定新键值对要添加到容器中的位置;val 参数指的是要添加容器中的键值对;方法的返回值为迭代器:

  • 如果 insert() 方法成功添加键值对,该迭代器指向新添加的键值对;
  • 如果 insert() 方法添加键值对失败,则表示容器中本就包含有相同键的键值对,该方法返回的迭代器就指向容器中键相同的键值对;

注意,以上 2 种语法格式中,虽然通过 hint 参数指定了新键值对添加到容器中的位置,但该键值对真正存储的位置,并不是 hint 参数说了算,最终的存储位置仍取决于该键值对的键的值。

举个例子:
#include
#include
#include <unordered_map>
using namespace std;
int main()
{
//创建空 umap 容器
unordered_map<string, string> umap;
//构建要添加的键值对
std::pair<string, string>mypair(“STL教程”, “http://c.biancheng.net/stl/”);
//创建接收 insert() 方法返回值的迭代器类型变量
unordered_map<string, string>::iterator iter;
//调用第一种语法格式
iter = umap.insert(umap.begin(), mypair);
cout << “iter -> " << iter->first <<” " << iter->second << endl;

//调用第二种语法格式
iter = umap.insert(umap.begin(),std::make_pair(“Python教程”, “http://c.biancheng.net/python/”));
cout << "iter -> " << iter->first << " " << iter->second << endl;
return 0;
}

程序输出结果为:

iter -> STL教程 http://c.biancheng.net/stl/
iter -> Python教程 http://c.biancheng.net/python/

  1. insert() 方法还支持将某一个 unordered_map 容器中指定区域内的所有键值对,复制到另一个 unordered_map 容器中,其语法格式如下:

template
void insert ( InputIterator first, InputIterator last );

其中 first 和 last 都为迭代器,[first, last)表示复制其它 unordered_map 容器中键值对的区域。

举个例子:

#include
#include
#include <unordered_map>
using namespace std;
int main()
{
//创建并初始化 umap 容器
unordered_map<string, string> umap{ {“STL教程”,“http://c.biancheng.net/stl/”},
{“Python教程”,“http://c.biancheng.net/python/”},
{“Java教程”,“http://c.biancheng.net/java/”} };
//创建一个空的 unordered_map 容器
unordered_map<string, string> otherumap;
//指定要拷贝 umap 容器中键值对的范围
unordered_map<string, string>::iterator first = ++umap.begin();
unordered_map<string, string>::iterator last = umap.end();
//将指定 umap 容器中 [first,last) 区域内的键值对复制给 otherumap 容器
otherumap.insert(first, last);
//遍历 otherumap 容器中存储的键值对
for (auto iter = otherumap.begin(); iter != otherumap.end(); ++iter){
cout << iter->first << " " << iter->second << endl;
}
return 0;
}

程序输出结果为:

Python教程 http://c.biancheng.net/python/
Java教程 http://c.biancheng.net/java/

  1. 除了以上 3 种方式,insert() 方法还支持一次向 unordered_map 容器添加多个键值对,其语法格式如下:

void insert ( initializer_list<value_type> il );

其中,il 参数指的是可以用初始化列表的形式指定多个键值对元素。

举个例子:

#include
#include
#include <unordered_map>
using namespace std;
int main()
{
//创建空的 umap 容器
unordered_map<string, string> umap;
//向 umap 容器同时添加多个键值对
umap.insert({ {“STL教程”,“http://c.biancheng.net/stl/”},
{“Python教程”,“http://c.biancheng.net/python/”},
{“Java教程”,“http://c.biancheng.net/java/”} });
//遍历输出 umap 容器中存储的键值对
for (auto iter = umap.begin(); iter != umap.end(); ++iter){
cout << iter->first << " " << iter->second << endl;
}
return 0;
}

程序输出结果为:

STL教程 http://c.biancheng.net/stl/
Python教程 http://c.biancheng.net/python/
Java教程 http://c.biancheng.net/java/

总的来说,unordered_map 模板类提供的 insert() 方法,有以上 4 种用法,读者可以根据实际场景的需要自行选择使用哪一种。

C++ STL unordered_map删除元素:erase()和clear()

C++ STL 标准库为了方便用户可以随时删除 unordered_map 容器中存储的键值对,unordered_map 容器类模板中提供了以下 2 个成员方法:

  • erase():删除 unordered_map 容器中指定的键值对;
  • clear():删除 unordered_map 容器中所有的键值对,即清空容器。

本节就对以上 2 个成员方法的用法做详细的讲解。

unordered_map erase()方法

为了满足不同场景删除 unordered_map 容器中键值对的需要,此容器的类模板中提供了 3 种语法格式的 erase() 方法。

  1. erase() 方法可以接受一个正向迭代器,并删除该迭代器指向的键值对。该方法的语法格式如下:

iterator erase ( const_iterator position );

其中 position 为指向容器中某个键值对的迭代器,该方法会返回一个指向被删除键值对之后位置的迭代器。

举个例子:

#include
#include
#include <unordered_map>
using namespace std;
int main()
{
//创建 umap 容器
unordered_map<string, string> umap{
{“STL教程”, “http://c.biancheng.net/stl/”},
{“Python教程”, “http://c.biancheng.net/python/”},
{“Java教程”, “http://c.biancheng.net/java/”} };
//输出 umap 容器中存储的键值对
for (auto iter = umap.begin(); iter != umap.end(); ++iter) {
cout << iter->first << " " << iter->second << endl;
}
cout << “erase:” << endl;
//定义一个接收 erase() 方法的迭代器
unordered_map<string,string>::iterator ret;
//删除容器中第一个键值对
ret = umap.erase(umap.begin());
//输出 umap 容器中存储的键值对
for (auto iter = umap.begin(); iter != umap.end(); ++iter) {
cout << iter->first << " " << iter->second << endl;
}
cout << "ret = " << ret->first << " " << ret->second << endl;
return 0;
}

程序执行结果为:

STL教程 http://c.biancheng.net/stl/
Python教程 http://c.biancheng.net/python/
Java教程 http://c.biancheng.net/java/
erase:
Python教程 http://c.biancheng.net/python/
Java教程 http://c.biancheng.net/java/
ret = Python教程 http://c.biancheng.net/python/

可以看到,通过给 erase() 方法传入指向容器中第一个键值对的迭代器,该方法可以将容器中第一个键值对删除,同时返回一个指向被删除键值对之后位置的迭代器。

注意,如果erase()方法删除的是容器存储的最后一个键值对,则该方法返回的迭代器,将指向容器中最后一个键值对之后的位置(等同于 end() 方法返回的迭代器)。

  1. 我们还可以直接将要删除键值对的键作为参数直接传给 erase() 方法,该方法会自行去 unordered_map 容器中找和给定键相同的键值对,将其删除。erase() 方法的语法格式如下:

size_type erase ( const key_type& k );

其中,k 表示目标键值对的键的值;该方法会返回一个整数,其表示成功删除的键值对的数量。

举个例子:

#include
#include
#include <unordered_map>
using namespace std;
int main()
{
//创建 umap 容器
unordered_map<string, string> umap{
{“STL教程”, “http://c.biancheng.net/stl/”},
{“Python教程”, “http://c.biancheng.net/python/”},
{“Java教程”, “http://c.biancheng.net/java/”} };
//输出 umap 容器中存储的键值对
for (auto iter = umap.begin(); iter != umap.end(); ++iter) {
cout << iter->first << " " << iter->second << endl;
}
int delNum = umap.erase(“Python教程”);
cout << "delNum = " << delNum << endl;
//再次输出 umap 容器中存储的键值对
for (auto iter = umap.begin(); iter != umap.end(); ++iter) {
cout << iter->first << " " << iter->second << endl;
}
return 0;
}

程序执行结果为:

STL教程 http://c.biancheng.net/stl/
Python教程 http://c.biancheng.net/python/
Java教程 http://c.biancheng.net/java/
delNum = 1
STL教程 http://c.biancheng.net/stl/
Java教程 http://c.biancheng.net/java/

通过输出结果可以看到,通过将 “Python教程” 传给 erase() 方法,就成功删除了 umap 容器中键为 “Python教程” 的键值对。

  1. 除了支持删除 unordered_map 容器中指定的某个键值对,erase() 方法还支持一次删除指定范围内的所有键值对,其语法格式如下:

iterator erase ( const_iterator first, const_iterator last );

其中 first 和 last 都是正向迭代器,[first, last) 范围内的所有键值对都会被 erase() 方法删除;同时,该方法会返回一个指向被删除的最后一个键值对之后一个位置的迭代器。

举个例子:

#include
#include
#include <unordered_map>
using namespace std;
int main()
{
//创建 umap 容器
unordered_map<string, string> umap{
{“STL教程”, “http://c.biancheng.net/stl/”},
{“Python教程”, “http://c.biancheng.net/python/”},
{“Java教程”, “http://c.biancheng.net/java/”} };
//first 指向第一个键值对
unordered_map<string, string>::iterator first = umap.begin();
//last 指向最后一个键值对
unordered_map<string, string>::iterator last = umap.end();
//删除[fist,last)范围内的键值对
auto ret = umap.erase(first, last);
//输出 umap 容器中存储的键值对
for (auto iter = umap.begin(); iter != umap.end(); ++iter) {
cout << iter->first << " " << iter->second << endl;
}
return 0;
}

执行程序会发现,没有输出任何数据,因为 erase() 方法删除了 umap 容器中 [begin(), end()) 范围内所有的元素。

unordered_map clear()方法

在个别场景中,可能需要一次性删除 unordered_map 容器中存储的所有键值对,可以使用 clear() 方法,其语法格式如下:

void clear()

举个例子:

#include
#include
#include <unordered_map>
using namespace std;
int main()
{
//创建 umap 容器
unordered_map<string, string> umap{
{“STL教程”, “http://c.biancheng.net/stl/”},
{“Python教程”, “http://c.biancheng.net/python/”},
{“Java教程”, “http://c.biancheng.net/java/”} };
//输出 umap 容器中存储的键值对
for (auto iter = umap.begin(); iter != umap.end(); ++iter) {
cout << iter->first << " " << iter->second << endl;
}
//删除容器内所有键值对
umap.clear();
cout << "umap size = " << umap.size() << endl;
return 0;
}

程序执行结果为:

STL教程 http://c.biancheng.net/stl/
Python教程 http://c.biancheng.net/python/
Java教程 http://c.biancheng.net/java/
umap size = 0

显然,通过调用 clear() 方法,原本包含 3 个键值对的 umap 容器,变成了空容器。

注意,虽然使用 erase() 方法的第 3 种语法格式,可能实现删除 unordered_map 容器内所有的键值对,但更推荐使用 clear() 方法。

C++ STL unordered_multimap容器精讲

C++ STL 标准库中,除了提供有 unordered_map 无序关联容器,还提供有和 unordered_map 容器非常相似的 unordered_multimap 无序关联容器。

和 unordered_map 容器一样,unordered_multimap 容器也以键值对的形式存储数据,且底层也采用哈希表结构存储各个键值对。两者唯一的不同之处在于,unordered_multimap 容器可以存储多个键相等的键值对,而 unordered_map 容器不行。

《深度剖析C++ STL无序容器底层原理》一文提到,无序容器中存储的各个键值对,都会哈希存到各个桶(本质为链表)中。而对于 unordered_multimap 容器来说,其存储的所有键值对中,键相等的键值对会被哈希到同一个桶中存储。

另外值得一提得是,STL 标准库中实现 unordered_multimap 容器的模板类并没有定义在以自己名称命名的头文件中,而是和 unordered_map 容器一样,定义在<unordered_map>头文件,且位于 std 命名空间中。因此,在使用 unordered_multimap 容器之前,程序中应包含如下 2 行代码:

#include <unordered_map>
using namespace std;

注意,第二行代码不是必需的,但如果不用,则后续程序中在使用此容器时,需手动注明 std 命名空间(强烈建议初学者使用)。

unordered_multimap 容器模板的定义如下所示:

template < class Key, //键(key)的类型
class T, //值(value)的类型
class Hash = hash, //底层存储键值对时采用的哈希函数
class Pred = equal_to, //判断各个键值对的键相等的规则
class Alloc = allocator< pair<const Key,T> > // 指定分配器对象的类型

class unordered_multimap;

以上 5 个参数中,必须显式给前 2 个参数传值,且除极个别的情况外,最多只使用前 4 个参数,它们各自的含义和功能如表 1 所示。

参数 含义
<key,T> 前 2 个参数分别用于确定键值对中键和值的类型,也就是存储键值对的类型。
Hash = hash 用于指明容器在存储各个键值对时要使用的哈希函数,默认使用 STL 标准库提供的 hash 哈希函数。注意,默认哈希函数只适用于基本数据类型(包括 string 类型),而不适用于自定义的结构体或者类。
Pred = equal_to unordered_multimap 容器可以存储多个键相等的键值对,而判断是否相等的规则,由此参数指定。默认情况下,使用 STL 标准库中提供的 equal_to 规则,该规则仅支持可直接用 == 运算符做比较的数据类型。

注意,当 unordered_multimap 容器中存储键值对的键为自定义类型时,默认的哈希函数 hash 以及比较函数 equal_to 将不再适用,这种情况下,需要我们自定义适用的哈希函数和比较函数,并分别显式传递给 Hash 参数和 Pred 参数。

关于给 unordered_multimap 容器自定义哈希函数和比较函数的方法,后续章节会做详细讲解。

创建C++ unordered_multimap容器的方法

常见的创建 unordered_map 容器的方法有以下几种。

  1. 利用 unordered_multimap 容器类模板中的默认构造函数,可以创建空的 unordered_multimap 容器。比如:

std::unordered_multimap<std::string, std::string>myummap;

如果程序中已经默认指定了 std 命令空间,这里可以省略所有的 std::。

由此,就创建好了一个可存储 <string, string> 类型键值对的 unordered_multimap 容器,只不过当前容器是空的,即没有存储任何键值对。

  1. 当然,在创建空 unordered_multimap 容器的基础上,可以完成初始化操作。比如:

unordered_multimap<string, string>myummap{
{“Python教程”,“http://c.biancheng.net/python/”},
{“Java教程”,“http://c.biancheng.net/java/”},
{“Linux教程”,“http://c.biancheng.net/linux/”} };

通过此方法创建的 myummap 容器中,就包含有 3 个键值对。

  1. 另外,unordered_multimap 模板中还提供有复制(拷贝)构造函数,可以实现在创建 unordered_multimap 容器的基础上,用另一 unordered_multimap 容器中的键值对为其初始化。

例如,在第二种方式创建好 myummap 容器的基础上,再创建并初始化一个 myummap2 容器:

unordered_multimap<string, string>myummap2(myummap);

由此,刚刚创建好的 myummap2 容器中,就包含有 myummap 容器中所有的键值对。

除此之外,C++ 11 标准中还向 unordered_multimap 模板类增加了移动构造函数,即以右值引用的方式将临时 unordered_multimap 容器中存储的所有键值对,全部复制给新建容器。例如:

//返回临时 unordered_multimap 容器的函数
std::unordered_multimap <std::string, std::string > retUmmap() {
std::unordered_multimap<std::string, std::string>tempummap{
{“Python教程”,“http://c.biancheng.net/python/”},
{“Java教程”,“http://c.biancheng.net/java/”},
{“Linux教程”,“http://c.biancheng.net/linux/”} };
return tempummap;
}
//创建并初始化 myummap 容器
std::unordered_multimap<std::string, std::string> myummap(retummap());

注意,无论是调用复制构造函数还是拷贝构造函数,必须保证 2 个容器的类型完全相同。

  1. 当然,如果不想全部拷贝,可以使用 unordered_multimap 类模板提供的迭代器,在现有 unordered_multimap 容器中选择部分区域内的键值对,为新建 unordered_multimap 容器初始化。例如:

//传入 2 个迭代器,
std::unordered_multimap<std::string, std::string> myummap2(++myummap.begin(), myummap.end());

通过此方式创建的 myummap2 容器,其内部就包含 myummap 容器中除第 1 个键值对外的所有其它键值对。

C++ unordered_multimap容器的成员方法

和 unordered_map 容器相比,unordered_multimap 容器的类模板中没有重载 [ ] 运算符,也没有提供 at() 成员方法,除此之外它们完全一致。

没有提供 [ ] 运算符和 at() 成员方法,意味着 unordered_multimap 容器无法通过指定键获取该键对应的值,因为该容器允许存储多个键相等的键值对,每个指定的键可能对应多个不同的值。

unordered_multimap 类模板提供的成员方法如表 2 所示。

成员方法 功能
begin() 返回指向容器中第一个键值对的正向迭代器。
end() 返回指向容器中最后一个键值对之后位置的正向迭代器。
cbegin() 和 begin() 功能相同,只不过在其基础上增加了 const 属性,即该方法返回的迭代器不能用于修改容器内存储的键值对。
cend() 和 end() 功能相同,只不过在其基础上,增加了 const 属性,即该方法返回的迭代器不能用于修改容器内存储的键值对。
empty() 若容器为空,则返回 true;否则 false。
size() 返回当前容器中存有键值对的个数。
max_size() 返回容器所能容纳键值对的最大个数,不同的操作系统,其返回值亦不相同。
find(key) 查找以 key 为键的键值对,如果找到,则返回一个指向该键值对的正向迭代器;反之,则返回一个指向容器中最后一个键值对之后位置的迭代器(如果 end() 方法返回的迭代器)。
count(key) 在容器中查找以 key 键的键值对的个数。
equal_range(key) 返回一个 pair 对象,其包含 2 个迭代器,用于表明当前容器中键为 key 的键值对所在的范围。
emplace() 向容器中添加新键值对,效率比 insert() 方法高。
emplace_hint() 向容器中添加新键值对,效率比 insert() 方法高。
insert() 向容器中添加新键值对。
erase() 删除指定键值对。
clear() 清空容器,即删除容器中存储的所有键值对。
swap() 交换 2 个 unordered_multimap 容器存储的键值对,前提是必须保证这 2 个容器的类型完全相等。
bucket_count() 返回当前容器底层存储键值对时,使用桶(一个线性链表代表一个桶)的数量。
max_bucket_count() 返回当前系统中,unordered_multimap 容器底层最多可以使用多少桶。
bucket_size(n) 返回第 n 个桶中存储键值对的数量。
bucket(key) 返回以 key 为键的键值对所在桶的编号。
load_factor() 返回 unordered_multimap 容器中当前的负载因子。负载因子,指的是的当前容器中存储键值对的数量(size())和使用桶数(bucket_count())的比值,即 load_factor() = size() / bucket_count()。
max_load_factor() 返回或者设置当前 unordered_multimap 容器的负载因子。
rehash(n) 将当前容器底层使用桶的数量设置为 n。
reserve() 将存储桶的数量(也就是 bucket_count() 方法的返回值)设置为至少容纳count个元(不超过最大负载因子)所需的数量,并重新整理容器。
hash_function() 返回当前容器使用的哈希函数对象。

注意,对于实现互换 2 个相同类型 unordered_multimap 容器的键值对,除了可以调用该容器模板类中提供的 swap() 成员方法外,STL 标准库还提供了同名的 swap() 非成员函数。

下面的样例演示了表 2 中部分成员方法的用法:

#include
#include
#include <unordered_map>
using namespace std;
int main()
{
//创建空容器
std::unordered_multimap<std::string, std::string> myummap;
//向空容器中连续添加 5 个键值对
myummap.emplace(“Python教程”, “http://c.biancheng.net/python/”);
myummap.emplace(“STL教程”, “http://c.biancheng.net/stl/”);
myummap.emplace(“Java教程”, “http://c.biancheng.net/java/”);
myummap.emplace(“C教程”, “http://c.biancheng.net”);
myummap.emplace(“C教程”, “http://c.biancheng.net/c/”);
//输出 muummap 容器存储键值对的个数
cout << "myummmap size = " << myummap.size() << endl;
//利用迭代器输出容器中存储的所有键值对
for (auto iter = myummap.begin(); iter != myummap.end(); ++iter) {
cout << iter->first << " " << iter->second << endl;
}
return 0;
}

程序执行结果为:

myummmap size = 5
Python教程 http://c.biancheng.net/python/
C教程 http://c.biancheng.net
C教程 http://c.biancheng.net/c/
STL教程 http://c.biancheng.net/stl/
Java教程 http://c.biancheng.net/java/

值得一提的是,unordered_multimap 模板提供的所有成员方法的用法,都和 unordered_map 提供的同名成员方法的用法完全相同(仅是调用者发生了改变),由于在讲解 unordered_map 容器时,已经对大部分成员方法的用法做了详细的讲解,后续不再做重复性地赘述。

C++ STL unordered_set容器完全攻略

我们知道,C++ 11 为 STL 标准库增添了 4 种无序(哈希)容器,前面已经对 unordered_map 和 unordered_multimap 容器做了详细的介绍,本节再讲解一种无序容器,即 unordered_set 容器。

unordered_set 容器,可直译为“无序 set 容器”,即 unordered_set 容器和 set 容器很像,唯一的区别就在于 set 容器会自行对存储的数据进行排序,而 unordered_set 容器不会。

总的来说,unordered_set 容器具有以下几个特性:

  1. 不再以键值对的形式存储数据,而是直接存储数据的值;
  2. 容器内部存储的各个元素的值都互不相等,且不能被修改。
  3. 不会对内部存储的数据进行排序(这和该容器底层采用哈希表结构存储数据有关,可阅读《C++ STL无序容器底层实现原理》一文做详细了解);

对于 unordered_set 容器不以键值对的形式存储数据,读者也可以这样认为,即 unordered_set 存储的都是键和值相等的键值对,为了节省存储空间,该类容器在实际存储时选择只存储每个键值对的值。

另外,实现 unordered_set 容器的模板类定义在<unordered_set>头文件,并位于 std 命名空间中。这意味着,如果程序中需要使用该类型容器,则首先应该包含如下代码:

#include <unordered_set>
using namespace std;

注意,第二行代码不是必需的,但如果不用,则程序中只要用到该容器时,必须手动注明 std 命名空间(强烈建议初学者使用)。

unordered_set 容器的类模板定义如下:

template < class Key, //容器中存储元素的类型
class Hash = hash, //确定元素存储位置所用的哈希函数
class Pred = equal_to, //判断各个元素是否相等所用的函数
class Alloc = allocator //指定分配器对象的类型

class unordered_set;

可以看到,以上 4 个参数中,只有第一个参数没有默认值,这意味着如果我们想创建一个 unordered_set 容器,至少需要手动传递 1 个参数。事实上,在 99% 的实际场景中最多只需要使用前 3 个参数(各自含义如表 1 所示),最后一个参数保持默认值即可。

参数 含义
Key 确定容器存储元素的类型,如果读者将 unordered_set 看做是存储键和值相同的键值对的容器,则此参数则用于确定各个键值对的键和值的类型,因为它们是完全相同的,因此一定是同一数据类型的数据。
Hash = hash 指定 unordered_set 容器底层存储各个元素时,所使用的哈希函数。需要注意的是,默认哈希函数 hash 只适用于基本数据类型(包括 string 类型),而不适用于自定义的结构体或者类。
Pred = equal_to unordered_set 容器内部不能存储相等的元素,而衡量 2 个元素是否相等的标准,取决于该参数指定的函数。 默认情况下,使用 STL 标准库中提供的 equal_to 规则,该规则仅支持可直接用 == 运算符做比较的数据类型。

注意,如果 unordered_set 容器中存储的元素为自定义的数据类型,则默认的哈希函数 hash 以及比较函数 equal_to 将不再适用,只能自己设计适用该类型的哈希函数和比较函数,并显式传递给 Hash 参数和 Pred 参数。至于如何实现自定义,后续章节会做详细讲解。

创建C++ unordered_set容器

前面介绍了如何创建 unordered_map 和 unordered_multimap 容器,值得一提的是,创建它们的所有方式完全适用于 unordereded_set 容器。不过,考虑到一些读者可能尚未学习其它无序容器,因此这里还是讲解一下创建 unordered_set 容器的几种方法。

  1. 通过调用 unordered_set 模板类的默认构造函数,可以创建空的 unordered_set 容器。比如:

std::unordered_setstd::string uset;

如果程序已经引入了 std 命名空间,这里可以省略所有的 std::。

由此,就创建好了一个可存储 string 类型值的 unordered_set 容器,该容器底层采用默认的哈希函数 hash 和比较函数 equal_to。

  1. 当然,在创建 unordered_set 容器的同时,可以完成初始化操作。比如:

std::unordered_setstd::string uset{ “http://c.biancheng.net/c/”,
“http://c.biancheng.net/java/”,
“http://c.biancheng.net/linux/” };

通过此方法创建的 uset 容器中,就包含有 3 个 string 类型元素。

  1. 还可以调用 unordered_set 模板中提供的复制(拷贝)构造函数,将现有 unordered_set 容器中存储的元素全部用于为新建 unordered_set 容器初始化。

例如,在第二种方式创建好 uset 容器的基础上,再创建并初始化一个 uset2 容器:

std::unordered_setstd::string uset2(uset);

由此,umap2 容器中就包含有 umap 容器中所有的元素。

除此之外,C++ 11 标准中还向 unordered_set 模板类增加了移动构造函数,即以右值引用的方式,利用临时 unordered_set 容器中存储的所有元素,给新建容器初始化。例如:

//返回临时 unordered_set 容器的函数
std::unordered_set std::string retuset() {
std::unordered_setstd::string tempuset{ “http://c.biancheng.net/c/”,
“http://c.biancheng.net/java/”,
“http://c.biancheng.net/linux/” };
return tempuset;
}
//调用移动构造函数,创建 uset 容器
std::unordered_setstd::string uset(retuset());

注意,无论是调用复制构造函数还是拷贝构造函数,必须保证 2 个容器的类型完全相同。

  1. 当然,如果不想全部拷贝,可以使用 unordered_set 类模板提供的迭代器,在现有 unordered_set 容器中选择部分区域内的元素,为新建 unordered_set 容器初始化。例如:

//传入 2 个迭代器,
std::unordered_setstd::string uset2(++uset.begin(),uset.end());

通过此方式创建的 uset2 容器,其内部就包含 uset 容器中除第 1 个元素外的所有其它元素。

C++ unordered_set容器的成员方法

unordered_set 类模板中,提供了如表 2 所示的成员方法。

成员方法 功能
begin() 返回指向容器中第一个元素的正向迭代器。
end(); 返回指向容器中最后一个元素之后位置的正向迭代器。
cbegin() 和 begin() 功能相同,只不过其返回的是 const 类型的正向迭代器。
cend() 和 end() 功能相同,只不过其返回的是 const 类型的正向迭代器。
empty() 若容器为空,则返回 true;否则 false。
size() 返回当前容器中存有元素的个数。
max_size() 返回容器所能容纳元素的最大个数,不同的操作系统,其返回值亦不相同。
find(key) 查找以值为 key 的元素,如果找到,则返回一个指向该元素的正向迭代器;反之,则返回一个指向容器中最后一个元素之后位置的迭代器(如果 end() 方法返回的迭代器)。
count(key) 在容器中查找值为 key 的元素的个数。
equal_range(key) 返回一个 pair 对象,其包含 2 个迭代器,用于表明当前容器中值为 key 的元素所在的范围。
emplace() 向容器中添加新元素,效率比 insert() 方法高。
emplace_hint() 向容器中添加新元素,效率比 insert() 方法高。
insert() 向容器中添加新元素。
erase() 删除指定元素。
clear() 清空容器,即删除容器中存储的所有元素。
swap() 交换 2 个 unordered_map 容器存储的元素,前提是必须保证这 2 个容器的类型完全相等。
bucket_count() 返回当前容器底层存储元素时,使用桶(一个线性链表代表一个桶)的数量。
max_bucket_count() 返回当前系统中,unordered_map 容器底层最多可以使用多少桶。
bucket_size(n) 返回第 n 个桶中存储元素的数量。
bucket(key) 返回值为 key 的元素所在桶的编号。
load_factor() 返回 unordered_map 容器中当前的负载因子。负载因子,指的是的当前容器中存储元素的数量(size())和使用桶数(bucket_count())的比值,即 load_factor() = size() / bucket_count()。
max_load_factor() 返回或者设置当前 unordered_map 容器的负载因子。
rehash(n) 将当前容器底层使用桶的数量设置为 n。
reserve() 将存储桶的数量(也就是 bucket_count() 方法的返回值)设置为至少容纳count个元(不超过最大负载因子)所需的数量,并重新整理容器。
hash_function() 返回当前容器使用的哈希函数对象。

注意,此容器模板类中没有重载 [ ] 运算符,也没有提供 at() 成员方法。不仅如此,由于 unordered_set 容器内部存储的元素值不能被修改,因此无论使用那个迭代器方法获得的迭代器,都不能用于修改容器中元素的值。

另外,对于实现互换 2 个相同类型 unordered_set 容器的所有元素,除了调用表 2 中的 swap() 成员方法外,还可以使用 STL 标准库提供的 swap() 非成员函数,它们具有相同的名称,用法也相同(都只需要传入 2 个参数即可),仅是调用方式上有差别。

下面的样例演示了表 2 中部分成员方法的用法:

#include
#include
#include <unordered_set>
using namespace std;
int main()
{
//创建一个空的unordered_set容器
std::unordered_setstd::string uset;
//给 uset 容器添加数据
uset.emplace(“http://c.biancheng.net/java/”);
uset.emplace(“http://c.biancheng.net/c/”);
uset.emplace(“http://c.biancheng.net/python/”);
//查看当前 uset 容器存储元素的个数
cout << "uset size = " << uset.size() << endl;
//遍历输出 uset 容器存储的所有元素
for (auto iter = uset.begin(); iter != uset.end(); ++iter) {
cout << *iter << endl;
}
return 0;
}

程序执行结果为:

uset size = 3
http://c.biancheng.net/java/
http://c.biancheng.net/c/
http://c.biancheng.net/python/

注意,表 2 中绝大多数成员方法的用法,都和 unordered_map 容器提供的同名成员方法相同,读者可翻阅前面的文章做详细了解,当然也可以到 C++
STL标准库官网
查询。

C++ STL unordered_multiset容器详解

前面章节详细地介绍了 unordered_set 容器的特定和用法,在此基础上,本节再介绍一个类似的 C++ STL 无序容器,即 unordered_multiset 容器。

所谓“类似”,指的是 unordered_multiset 容器大部分的特性都和 unordered_set 容器相同,包括:

  1. unordered_multiset 不以键值对的形式存储数据,而是直接存储数据的值;
  2. 该类型容器底层采用的也是哈希表存储结构(可阅读《C++ STL无序容器底层实现原理》一文做详细了解),它不会对内部存储的数据进行排序;
  3. unordered_multiset 容器内部存储的元素,其值不能被修改。

和 unordered_set 容器不同的是,unordered_multiset 容器可以同时存储多个值相同的元素,且这些元素会存储到哈希表中同一个桶(本质就是链表)上。

读者可以这样认为,unordered_multiset 除了能存储相同值的元素外,它和 unordered_set 容器完全相同。

另外值得一提的是,实现 unordered_multiset 容器的模板类并没有定义在以该容器名命名的文件中,而是和 unordered_set 容器共用同一个<unordered_set>头文件,并且也位于 std 命名空间。因此,如果程序中需要使用该类型容器,应包含如下代码:

#include <unordered_set>using namespace std;

注意,第二行代码不是必需的,但如果不用,则程序中只要用到该容器时,必须手动注明 std 命名空间(强烈建议初学者使用)。

unordered_multiset 容器类模板的定义如下:

template < class Key, //容器中存储元素的类型
class Hash = hash, //确定元素存储位置所用的哈希函数
class Pred = equal_to, //判断各个元素是否相等所用的函数
class Alloc = allocator //指定分配器对象的类型

class unordered_multiset;

需要说明的是,在 99% 的实际场景中,最多只需要使用前 3 个参数(各自含义如表 1 所示),最后一个参数保持默认值即可。

参数 含义
Key 确定容器存储元素的类型,如果读者将 unordered_multiset 看做是存储键和值相同的键值对的容器,则此参数则用于确定各个键值对的键和值的类型,因为它们是完全相同的,因此一定是同一数据类型的数据。
Hash = hash 指定 unordered_multiset 容器底层存储各个元素时所使用的哈希函数。需要注意的是,默认哈希函数 hash 只适用于基本数据类型(包括 string 类型),而不适用于自定义的结构体或者类。
Pred = equal_to 用于指定 unordered_multiset 容器判断元素值相等的规则。默认情况下,使用 STL 标准库中提供的 equal_to 规则,该规则仅支持可直接用 == 运算符做比较的数据类型。

总之,如果 unordered_multiset 容器中存储的元素为自定义的数据类型,则默认的哈希函数 hash 以及比较函数 equal_to 将不再适用,只能自己设计适用该类型的哈希函数和比较函数,并显式传递给 Hash 参数和 Pred 参数。至于如何实现自定义,后续章节会做详细讲解。

创建C++ unordered_multiset容器

考虑到不同场景的需要,unordered_multiset 容器模板类共提供了以下 4 种创建 unordered_multiset 容器的方式。

  1. 调用 unordered_multiset 模板类的默认构造函数,可以创建空的 unordered_multiset 容器。比如:

std::unordered_multisetstd::string umset;

如果程序已经引入了 std 命名空间,这里可以省略所有的 std::。

由此,就创建好了一个可存储 string 类型值的 unordered_multiset 容器,该容器底层采用默认的哈希函数 hash 和比较函数 equal_to。

  1. 当然,在创建 unordered_multiset 容器的同时,可以进行初始化操作。比如:

std::unordered_multisetstd::string umset{ “http://c.biancheng.net/c/”,
“http://c.biancheng.net/java/”,
“http://c.biancheng.net/linux/” };

通过此方法创建的 umset 容器中,内部存有 3 个 string 类型元素。

  1. 还可以调用 unordered_multiset 模板中提供的复制(拷贝)构造函数,将现有 unordered_multiset 容器中存储的元素全部用于为新建 unordered_multiset 容器初始化。

例如,在第二种方式创建好 umset 容器的基础上,再创建并初始化一个 umset2 容器:

std::unordered_multisetstd::string umset2(umset);

由此,umap2 容器中就包含有 umap 容器中所有的元素。

除此之外,C++ 11 标准中还向 unordered_multiset 模板类增加了移动构造函数,即以右值引用的方式,利用临时 unordered_multiset 容器中存储的所有元素,给新建容器初始化。例如:

//返回临时 unordered_multiset 容器的函数
std::unordered_multiset std::string retumset() {
std::unordered_multisetstd::string tempumset{ “http://c.biancheng.net/c/”,
“http://c.biancheng.net/java/”,
“http://c.biancheng.net/linux/” };
return tempumset;
}
//调用移动构造函数,创建 umset 容器
std::unordered_multisetstd::string umset(retumset());

注意,无论是调用复制构造函数还是拷贝构造函数,必须保证 2 个容器的类型完全相同。

  1. 当然,如果不想全部拷贝,可以使用 unordered_multiset 类模板提供的迭代器,在现有 unordered_multiset 容器中选择部分区域内的元素,为新建 unordered_multiset 容器初始化。例如:

//传入 2 个迭代器,
std::unordered_multisetstd::string umset2(++umset.begin(), umset.end());

通过此方式创建的 umset2 容器,其内部就包含 umset 容器中除第 1 个元素外的所有其它元素。

C++ unordered_multimap容器的成员方法

值得一提的是,unordered_multiset 模板类中提供的成员方法,无论是种类还是数量,都和 unordered_set 类模板一样,如表 2 所示。

成员方法 功能
begin() 返回指向容器中第一个元素的正向迭代器。
end(); 返回指向容器中最后一个元素之后位置的正向迭代器。
cbegin() 和 begin() 功能相同,只不过其返回的是 const 类型的正向迭代器。
cend() 和 end() 功能相同,只不过其返回的是 const 类型的正向迭代器。
empty() 若容器为空,则返回 true;否则 false。
size() 返回当前容器中存有元素的个数。
max_size() 返回容器所能容纳元素的最大个数,不同的操作系统,其返回值亦不相同。
find(key) 查找以值为 key 的元素,如果找到,则返回一个指向该元素的正向迭代器;反之,则返回一个指向容器中最后一个元素之后位置的迭代器(如果 end() 方法返回的迭代器)。
count(key) 在容器中查找值为 key 的元素的个数。
equal_range(key) 返回一个 pair 对象,其包含 2 个迭代器,用于表明当前容器中值为 key 的元素所在的范围。
emplace() 向容器中添加新元素,效率比 insert() 方法高。
emplace_hint() 向容器中添加新元素,效率比 insert() 方法高。
insert() 向容器中添加新元素。
erase() 删除指定元素。
clear() 清空容器,即删除容器中存储的所有元素。
swap() 交换 2 个 unordered_multimap 容器存储的元素,前提是必须保证这 2 个容器的类型完全相等。
bucket_count() 返回当前容器底层存储元素时,使用桶(一个线性链表代表一个桶)的数量。
max_bucket_count() 返回当前系统中,容器底层最多可以使用多少桶。
bucket_size(n) 返回第 n 个桶中存储元素的数量。
bucket(key) 返回值为 key 的元素所在桶的编号。
load_factor() 返回容器当前的负载因子。所谓负载因子,指的是的当前容器中存储元素的数量(size())和使用桶数(bucket_count())的比值,即 load_factor() = size() / bucket_count()。
max_load_factor() 返回或者设置当前 unordered_map 容器的负载因子。
rehash(n) 将当前容器底层使用桶的数量设置为 n。
reserve() 将存储桶的数量(也就是 bucket_count() 方法的返回值)设置为至少容纳count个元(不超过最大负载因子)所需的数量,并重新整理容器。
hash_function() 返回当前容器使用的哈希函数对象。

注意,和 unordered_set 容器一样,unordered_multiset 模板类也没有重载 [ ] 运算符,没有提供 at() 成员方法。不仅如此,无论是由哪个成员方法返回的迭代器,都不能用于修改容器中元素的值。

另外,对于互换 2 个相同类型 unordered_multiset 容器存储的所有元素,除了调用表 2 中的 swap() 成员方法外,STL 标准库也提供了 swap() 非成员函数。

下面的样例演示了表 2 中部分成员方法的用法:

#include
#include
#include <unordered_set>
using namespace std;
int main()
{
//创建一个空的unordered_multiset容器
std::unordered_multisetstd::string umset;
//给 uset 容器添加数据
umset.emplace(“http://c.biancheng.net/java/”);
umset.emplace(“http://c.biancheng.net/c/”);
umset.emplace(“http://c.biancheng.net/python/”);
umset.emplace(“http://c.biancheng.net/c/”);
//查看当前 umset 容器存储元素的个数
cout << "umset size = " << umset.size() << endl;
//遍历输出 umset 容器存储的所有元素
for (auto iter = umset.begin(); iter != umset.end(); ++iter) {
cout << *iter << endl;
}
return 0;
}

程序执行结果为:

umset size = 4
http://c.biancheng.net/java/
http://c.biancheng.net/c/
http://c.biancheng.net/c/
http://c.biancheng.net/python/

注意,表 2 中绝大多数成员方法的用法,都和 unordered_map 容器提供的同名成员方法相同,读者可翻阅前面的文章做详细了解,当然也可以到C++ STL标准库官网查询。

如何自定义C++ STL无序容器的哈希函数和比较规则?(超级详细)

前面在讲解 unordered_map、unordered_multimap、unordered_set 以及 unordered_multiset 这 4 种无序关联式容器(哈希容器)时,遗留过一个共性问题,即如何给无序容器自定义一个哈希函数和比较规则?

注意,虽然每种无序容器都指定了默认的 hash 哈希函数和 equal_to 比较规则,但它们仅适用于存储基本类型(比如 int、double、float、string 等)数据的无序容器。换句话说,如果无序容器存储的数据类型为自定义的结构体或类,则 STL 标准库提供的 hash 和 equal_to 将不再适用。

C++无序容器自定义哈希函数

我们知道,无序容器以键值对的方式存储数据(unordered_set 和 unordered_multiset 容器可以看做存储的是键和值相等的键值对),且底层采用哈希表结构存储各个键值对。在此存储结构中,哈希函数的功能是根据各个键值对中键的值,计算出一个哈希值(本质就是一个整数),哈希表可以根据该值判断出该键值对具体的存储位置。

简单地理解哈希函数,它可以接收一个元素,并通过内部对该元素做再加工,最终会得出一个整形值并反馈回来。需要注意的是,哈希函数只是一个称谓,其本体并不是普通的函数形式,而是一个函数对象类。因此,如果我们想自定义个哈希函数,就需要自定义一个函数对象类。

关于什么函数对象类,可阅读《C++函数对象详解》一节做详细了解,由于不是本节重点,这里不再赘述。

举个例子,假设有如下一个 Person 类:

class Person {
public:
Person(string name, int age) :name(name), age(age) {};
string getName() const;
int getAge() const;
private:
string name;
int age;
};
string Person::getName() const {
return this->name;
}
int Person::getAge() const {
return this->age;
}

在此基础上,假设我们想创建一个可存储 Person 类对象的 unordered_set 容器,考虑到 Person 为自定义的类型,因此默认的 hash 哈希函数不再适用,这时就需要以函数对象类的方式自定义一个哈希函数。比如:

class hash_fun {
public:
int operator()(const Person &A) const {
return A.getAge();
}
};

注意,重载 ( ) 运算符时,其参数必须为 const 类型,且该方法也必须用 const 修饰。

可以看到,我们利用 hash_fun 函数对象类的 ( ) 运算符重载方法,自定义了适用于 Person 类对象的哈希函数。该哈希函数每接收一个 Person 类对象,都会返回该对象的 age 成员变量的值。

事实上,默认的 hash 哈希函数,其底层也是以函数对象类的形式实现的。

由此,在创建存储 Person 类对象的 unordered_set 容器时,可以将 hash_fun 作为参数传递给该容器模板类中的 Pred 参数:

std::unordered_set<Person, hash_fun> myset;

但是,此时创建的 myset 容器还无法使用,因为该容器使用的是默认的 std::equal_to 比较规则,但此规则并不适用于该容器。

C++无序容器自定义比较规则

和哈希函数一样,无论创建哪种无序容器,都需要为其指定一种可比较容器中各个元素是否相等的规则。

值得一提的是,默认情况下无序容器使用的 std::equal_to 比较规则,其本质也是一个函数对象类,底层实现如下:

template
class equal_to
{
public:
bool operator()(const T& _Left, const T& _Right) const{
return (_Left == _Right);
}
};

可以看到,该规则在底层实现过程中,直接用 == 运算符比较容器中任意 2 个元素是否相等,这意味着,如果容器中存储的元素类型,支持直接用 == 运算符比较是否相等,则该容器可以使用默认的 std::equal_to 比较规则;反之,就不可以使用。

显然,对于我们上面创建的 myset 容器,其内部存储的是 Person 类对象,不支持直接使用 == 运算符做比较。这种情况下,有以下 2 种方式可以解决此问题:

  1. 在 Person 类中重载 == 运算符,这会使得 std::equal_to 比较规则中使用的 == 运算符变得合法,myset 容器就可以继续使用 std::equal_to 比较规则;
  2. 以函数对象类的方式,自定义一个适用于 myset 容器的比较规则。
1) 重载==运算符

如果选用第一种解决方式,仍以 Python 类为例,在此类的外部添加如下语句:

bool operator==(const Person &A, const Person &B) {
return (A.getAge() == B.getAge());
}

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
img

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
重载 ( ) 运算符时,其参数必须为 const 类型,且该方法也必须用 const 修饰。

可以看到,我们利用 hash_fun 函数对象类的 ( ) 运算符重载方法,自定义了适用于 Person 类对象的哈希函数。该哈希函数每接收一个 Person 类对象,都会返回该对象的 age 成员变量的值。

事实上,默认的 hash 哈希函数,其底层也是以函数对象类的形式实现的。

由此,在创建存储 Person 类对象的 unordered_set 容器时,可以将 hash_fun 作为参数传递给该容器模板类中的 Pred 参数:

std::unordered_set<Person, hash_fun> myset;

但是,此时创建的 myset 容器还无法使用,因为该容器使用的是默认的 std::equal_to 比较规则,但此规则并不适用于该容器。

C++无序容器自定义比较规则

和哈希函数一样,无论创建哪种无序容器,都需要为其指定一种可比较容器中各个元素是否相等的规则。

值得一提的是,默认情况下无序容器使用的 std::equal_to 比较规则,其本质也是一个函数对象类,底层实现如下:

template
class equal_to
{
public:
bool operator()(const T& _Left, const T& _Right) const{
return (_Left == _Right);
}
};

可以看到,该规则在底层实现过程中,直接用 == 运算符比较容器中任意 2 个元素是否相等,这意味着,如果容器中存储的元素类型,支持直接用 == 运算符比较是否相等,则该容器可以使用默认的 std::equal_to 比较规则;反之,就不可以使用。

显然,对于我们上面创建的 myset 容器,其内部存储的是 Person 类对象,不支持直接使用 == 运算符做比较。这种情况下,有以下 2 种方式可以解决此问题:

  1. 在 Person 类中重载 == 运算符,这会使得 std::equal_to 比较规则中使用的 == 运算符变得合法,myset 容器就可以继续使用 std::equal_to 比较规则;
  2. 以函数对象类的方式,自定义一个适用于 myset 容器的比较规则。
1) 重载==运算符

如果选用第一种解决方式,仍以 Python 类为例,在此类的外部添加如下语句:

bool operator==(const Person &A, const Person &B) {
return (A.getAge() == B.getAge());
}

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化的资料的朋友,可以添加V获取:vip1024b (备注Go)
[外链图片转存中…(img-LjSNTmuS-1713433624916)]

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/2401_84263403/article/details/137930584

智能推荐

什么是内部类?成员内部类、静态内部类、局部内部类和匿名内部类的区别及作用?_成员内部类和局部内部类的区别-程序员宅基地

文章浏览阅读3.4k次,点赞8次,收藏42次。一、什么是内部类?or 内部类的概念内部类是定义在另一个类中的类;下面类TestB是类TestA的内部类。即内部类对象引用了实例化该内部对象的外围类对象。public class TestA{ class TestB {}}二、 为什么需要内部类?or 内部类有什么作用?1、 内部类方法可以访问该类定义所在的作用域中的数据,包括私有数据。2、内部类可以对同一个包中的其他类隐藏起来。3、 当想要定义一个回调函数且不想编写大量代码时,使用匿名内部类比较便捷。三、 内部类的分类成员内部_成员内部类和局部内部类的区别

分布式系统_分布式系统运维工具-程序员宅基地

文章浏览阅读118次。分布式系统要求拆分分布式思想的实质搭配要求分布式系统要求按照某些特定的规则将项目进行拆分。如果将一个项目的所有模板功能都写到一起,当某个模块出现问题时将直接导致整个服务器出现问题。拆分按照业务拆分为不同的服务器,有效的降低系统架构的耦合性在业务拆分的基础上可按照代码层级进行拆分(view、controller、service、pojo)分布式思想的实质分布式思想的实质是为了系统的..._分布式系统运维工具

用Exce分析l数据极简入门_exce l趋势分析数据量-程序员宅基地

文章浏览阅读174次。1.数据源准备2.数据处理step1:数据表处理应用函数:①VLOOKUP函数; ② CONCATENATE函数终表:step2:数据透视表统计分析(1) 透视表汇总不同渠道用户数, 金额(2)透视表汇总不同日期购买用户数,金额(3)透视表汇总不同用户购买订单数,金额step3:讲第二步结果可视化, 比如, 柱形图(1)不同渠道用户数, 金额(2)不同日期..._exce l趋势分析数据量

宁盾堡垒机双因素认证方案_horizon宁盾双因素配置-程序员宅基地

文章浏览阅读3.3k次。堡垒机可以为企业实现服务器、网络设备、数据库、安全设备等的集中管控和安全可靠运行,帮助IT运维人员提高工作效率。通俗来说,就是用来控制哪些人可以登录哪些资产(事先防范和事中控制),以及录像记录登录资产后做了什么事情(事后溯源)。由于堡垒机内部保存着企业所有的设备资产和权限关系,是企业内部信息安全的重要一环。但目前出现的以下问题产生了很大安全隐患:密码设置过于简单,容易被暴力破解;为方便记忆,设置统一的密码,一旦单点被破,极易引发全面危机。在单一的静态密码验证机制下,登录密码是堡垒机安全的唯一_horizon宁盾双因素配置

谷歌浏览器安装(Win、Linux、离线安装)_chrome linux debian离线安装依赖-程序员宅基地

文章浏览阅读7.7k次,点赞4次,收藏16次。Chrome作为一款挺不错的浏览器,其有着诸多的优良特性,并且支持跨平台。其支持(Windows、Linux、Mac OS X、BSD、Android),在绝大多数情况下,其的安装都很简单,但有时会由于网络原因,无法安装,所以在这里总结下Chrome的安装。Windows下的安装:在线安装:离线安装:Linux下的安装:在线安装:离线安装:..._chrome linux debian离线安装依赖

烤仔TVの尚书房 | 逃离北上广?不如押宝越南“北上广”-程序员宅基地

文章浏览阅读153次。中国发达城市榜单每天都在刷新,但无非是北上广轮流坐庄。北京拥有最顶尖的文化资源,上海是“摩登”的国际化大都市,广州是活力四射的千年商都。GDP和发展潜力是衡量城市的数字指...

随便推点

java spark的使用和配置_使用java调用spark注册进去的程序-程序员宅基地

文章浏览阅读3.3k次。前言spark在java使用比较少,多是scala的用法,我这里介绍一下我在项目中使用的代码配置详细算法的使用请点击我主页列表查看版本jar版本说明spark3.0.1scala2.12这个版本注意和spark版本对应,只是为了引jar包springboot版本2.3.2.RELEASEmaven<!-- spark --> <dependency> <gro_使用java调用spark注册进去的程序

汽车零部件开发工具巨头V公司全套bootloader中UDS协议栈源代码,自己完成底层外设驱动开发后,集成即可使用_uds协议栈 源代码-程序员宅基地

文章浏览阅读4.8k次。汽车零部件开发工具巨头V公司全套bootloader中UDS协议栈源代码,自己完成底层外设驱动开发后,集成即可使用,代码精简高效,大厂出品有量产保证。:139800617636213023darcy169_uds协议栈 源代码

AUTOSAR基础篇之OS(下)_autosar 定义了 5 种多核支持类型-程序员宅基地

文章浏览阅读4.6k次,点赞20次,收藏148次。AUTOSAR基础篇之OS(下)前言首先,请问大家几个小小的问题,你清楚:你知道多核OS在什么场景下使用吗?多核系统OS又是如何协同启动或者关闭的呢?AUTOSAR OS存在哪些功能安全等方面的要求呢?多核OS之间的启动关闭与单核相比又存在哪些异同呢?。。。。。。今天,我们来一起探索并回答这些问题。为了便于大家理解,以下是本文的主题大纲:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JCXrdI0k-1636287756923)(https://gite_autosar 定义了 5 种多核支持类型

VS报错无法打开自己写的头文件_vs2013打不开自己定义的头文件-程序员宅基地

文章浏览阅读2.2k次,点赞6次,收藏14次。原因:自己写的头文件没有被加入到方案的包含目录中去,无法被检索到,也就无法打开。将自己写的头文件都放入header files。然后在VS界面上,右键方案名,点击属性。将自己头文件夹的目录添加进去。_vs2013打不开自己定义的头文件

【Redis】Redis基础命令集详解_redis命令-程序员宅基地

文章浏览阅读3.3w次,点赞80次,收藏342次。此时,可以将系统中所有用户的 Session 数据全部保存到 Redis 中,用户在提交新的请求后,系统先从Redis 中查找相应的Session 数据,如果存在,则再进行相关操作,否则跳转到登录页面。此时,可以将系统中所有用户的 Session 数据全部保存到 Redis 中,用户在提交新的请求后,系统先从Redis 中查找相应的Session 数据,如果存在,则再进行相关操作,否则跳转到登录页面。当数据量很大时,count 的数量的指定可能会不起作用,Redis 会自动调整每次的遍历数目。_redis命令

URP渲染管线简介-程序员宅基地

文章浏览阅读449次,点赞3次,收藏3次。URP的设计目标是在保持高性能的同时,提供更多的渲染功能和自定义选项。与普通项目相比,会多出Presets文件夹,里面包含着一些设置,包括本色,声音,法线,贴图等设置。全局只有主光源和附加光源,主光源只支持平行光,附加光源数量有限制,主光源和附加光源在一次Pass中可以一起着色。URP:全局只有主光源和附加光源,主光源只支持平行光,附加光源数量有限制,一次Pass可以计算多个光源。可编程渲染管线:渲染策略是可以供程序员定制的,可以定制的有:光照计算和光源,深度测试,摄像机光照烘焙,后期处理策略等等。_urp渲染管线

推荐文章

热门文章

相关标签