现代 C++ 编程实战
吴咏炜
前 Intel 资深软件架构师
34196 人已学习
新⼈⾸单¥59
登录后,你可以任选4讲全文学习
课程目录
已完结/共 51 讲
加餐 (1讲)
现代 C++ 编程实战
15
15
1.0x
00:00/00:00
登录|注册

05 | 容器汇编 II:需要函数对象的容器

你好,我是吴咏炜。
上一讲我们学习了 C++ 的序列容器和两个容器适配器,今天我们继续讲完剩下的标准容器([1])。

函数对象及其特化

在讲容器之前,我们需要首先来讨论一下两个重要的函数对象,lesshash
我们先看一下 less,小于关系。在标准库里,通用的 less 大致是这样定义的:
template <class T>
struct less
: binary_function<T, T, bool> {
bool operator()(const T& x,
const T& y) const
{
return x < y;
}
};
也就是说,less 是一个函数对象,并且是个二元函数,执行对任意类型的值的比较,返回布尔类型。作为函数对象,它定义了函数调用运算符(operator()),并且缺省行为是对指定类型的对象进行 < 的比较操作。
有点平淡无奇,是吧?原因是因为这个缺省实现在大部分情况下已经够用,我们不太需要去碰它。在需要大小比较的场合,C++ 通常默认会使用 less,包括我们今天会讲到的若干容器和排序算法 sort。如果我们需要产生相反的顺序的话,则可以使用 greater,大于关系。
计算哈希值的函数对象 hash 就不一样了。它的目的是把一个某种类型的值转换成一个无符号整数哈希值,类型为 size_t。它没有一个可用的默认实现。对于常用的类型,系统提供了需要的特化 [2],类似于:
确认放弃笔记?
放弃后所记笔记将不保留。
新功能上线,你的历史笔记已初始化为私密笔记,是否一键批量公开?
批量公开的笔记不会为你同步至部落
公开
同步至部落
取消
完成
0/2000
荧光笔
直线
曲线
笔记
复制
AI
  • 深入了解
  • 翻译
    • 英语
    • 中文简体
    • 中文繁体
    • 法语
    • 德语
    • 日语
    • 韩语
    • 俄语
    • 西班牙语
    • 阿拉伯语
  • 解释
  • 总结

本文深入介绍了C++中的函数对象以及需要函数对象的容器,以及关联容器和无序关联容器的使用方法和特点。文章首先讨论了`less`和`hash`两个重要的函数对象,分别用于比较大小关系和计算哈希值,并通过代码示例展示了它们的基本用法和特点。接着介绍了`priority_queue`容器适配器,演示了如何使用它以及如何传递不同的比较函数对象来改变容器的行为。随后,文章详细介绍了关联容器和无序关联容器的特点和使用方法,包括`set`、`map`、`multiset`、`multimap`、`unordered_set`、`unordered_map`、`unordered_multiset`和`unordered_multimap`等容器的特性和区别。通过代码示例展示了这些容器的基本操作和特点,以及如何使用自定义的哈希函数对象。总的来说,本文通过深入讲解函数对象和不同类型的容器,帮助读者全面了解了C++中的容器和函数对象的使用方法和特点,以及关联容器和无序关联容器的区别和优劣势。 文章还介绍了C++的两个常用的函数对象`less`和`hash`,以及用到这两个函数对象的容器适配器、关联容器和无序关联容器。最后,通过例子展示了为什么我们应当避免C数组而考虑使用`array`。通过这些内容,读者可以完整地了解C++提供的标准容器。 在阅读本文后,读者可以快速了解C++中函数对象和不同类型的容器的使用方法和特点,以及关联容器和无序关联容器的区别和优劣势。同时,读者还可以了解到为什么应该避免使用C数组,而考虑使用`array`。这篇文章对于想要深入了解C++容器和函数对象的读者来说是一份有价值的资料。

仅可试看部分内容,如需阅读全部内容,请付费购买文章所属专栏
《现代 C++ 编程实战》
新⼈⾸单¥59
立即购买
登录 后留言

全部留言(24)

  • 最新
  • 精选
  • 1. 首先是为了遍历容器方便,其次为了保证std接口的一致性 2. 我认为是没有必要的,因为,如果用类的继承一般会产生虚函数,这也就导致多存储一个虚函数表,在内存上产生了不必要的浪费。

    作者回复: 基本正确。 2 实际上还是因为这儿继承真的没啥用。有了泛型,继承基本是种浪费。而且,继承用基类的指针或引用才有用,而C++里的容器类一般都是当值类型来用的。

    2019-12-07
    3
    22
  • 皓首不倦
    老师您好 第一个问题个人觉得提供出头尾迭代器目的主要是提供给STL算法库用,STL算法库实现是只认迭代器的,这样的设计可以避免算法实现和具体容器类型的强耦合 第二个问题个人觉得是因为各种容器API和功能上差异比价大,难以做一层比较通用的抽象,但是像map和unordered_map这样功能比较相近的容器我觉的还是可以做一层抽象的,像Java中TreeMap和HashMap就同时实现了Map接口,做了一层抽象,其实我也一直没有弄懂C++为什么没有向Java一样对功能相近的容器做一层接口的抽象层,老师能帮我解答下吗?

    作者回复: 一,正确。 二,问题还在于C++的值语义。如果你用一个 AbstractMap来访问map和unordered_map,你只能用引用或指针,并不方便。而且,map和unordered_map的模板参数还不一样,通用的话反而成了阉割版……

    2019-12-09
    2
    9
  • qinsi
    既然关联容器的key需要满足strict weak ordering,那么sort的比较函数是不是也需要满足?比如sort(v.begin(), v.end(), less_equal<int>());是否可行?

    作者回复: 是,也必须满足。标准里明确这么要求了。 如果你传了 less_equal 这样的条件,结果可能是正确,也可能是错误,也可能是程序崩溃。具体发生什么,取决于排序算法的实现。出了问题,就是调用者的锅,不是 sort 的 bug。 以冒泡排序为例,我试了一下,如果是用检验有没有交换来决定是否退出排序,那么,在元素有重复的情况下使用 less_equal 来排序,会导致死循环。

    2019-12-07
    7
  • 啦啦啦
    想问一下,priority_queue这种,如果通过指针持有某个对象,然后传入比较函数,那么如果更新指针指向对象的值,priority_queue是否会更新?如果不会,有方法能够使它更新吗?

    作者回复: 不会也不可以。这些容器适配器不提供遍历的迭代器就是不允许像你描述这样的任意访问。(迭代器就是广义指针。)

    2019-12-10
    4
  • lyfei
    老师你好,那为什么unodered_map会使用到operator==的呢? 我感觉他不是应该把数据转到hash值,然后保存起来,也感觉没有比较的过程,哪个地方体现了==这个运算符呀?

    作者回复: 不是。哈希是哈希,哈希可能有冲突的,相同哈希值也要看键是相同还是不同,来决定是覆盖还是加一项。去看一下数据结构里面的哈希。

    2019-12-08
    2
    3
  • 中年男子
    1、begin、end是迭代器,主要用于对不同类型的容器提供统一的遍历容器的辅助 2不同容器内存分配方式不同,实现不同,基类方法无法做到统一,非要用继承只能定义虚函数 多用组合、少用继承(抖个机灵)

    作者回复: 哈哈,是这样的。

    2019-12-06
    3
  • 李义盛
    2.继承是强耦合。 继承导致关系混乱。

    作者回复: 算是一个点。真要继承,不会更好用,因为容器的接口差异往往很小,这儿多一个,那儿少一个,要多少接口才能表达啊。

    2019-12-06
    3
  • 贾陆华
    1. 看到一个注释笔误,是从大到小吧 // 从小到大排序 sort(v.begin(), v.end(), greater<int>()); cout << v << endl; 2. map.lower_bound(k)默认代表的意思是找到第一个x,x不小于k,map.upper_bound(k)默认是找到第一个x,x大于k,为什么不是x小于k,upper_bound字面意思不是上界吗?

    作者回复: 1. 哈,还真是的。典型的拷贝粘贴错误…… 2. 想一想,C++ 里到处是半开半闭区间。右界一直是会超出有效范围的。

    2019-12-07
    2
    2
  • 爱笑的布谷鸟
    请问老师,为什么 weak_ptr 不能作为 unordered_x 容器的 key?

    作者回复: weak_ptr里你要获取指针都得去lock一下,你该怎么去算哈希值和判断相等?不能算哈希值和判断相等,你以后怎么做查询?

    2022-09-01归属地:上海
    1
  • Geek_24c4df
    看不懂下面用法 unordered_map<complex<double>, double> umc{{{1.0, 1.0}, 1.4142}, {{3.0, 4.0}, 5.0}}; 为什么键里面还要套一层大括号数据

    作者回复: {1.0, 1.0} 可以用来构造一个复数(1 + i),{{1.0, 1.0}, 1.4142} 是 unordered_map 里的一项。

    2020-05-23
    1
收起评论
显示
设置
留言
24
收藏
沉浸
阅读
分享
手机端
快捷键
回顶部