1.基本概念与基础用法
vector,deque,list 这类容器内部的元素的(逻辑)存储次序, 由各元素插入选择的位置共同决定,调用一个push_back()插入新元素,新元素就肯定是在容器的尾部,除非事后我们调用sort操作。
但有时候我们希望一个容器能够依据某种规则,维护数据的有序性。
set和multiset都会根据既定的排序准则,在数据插入时,自动将元素排序,
但multiset允许大小相等的两个元素加入,
而set不允许。
测试表明:
一是multiset确实会自动将元素排序,
二是multiset确实允许输入大小一样的两个元素。
multiset默认对两个元素使用“<”比较大小,小排前,大排后,所以本例需要为自定义类型Student提供“<”的重载。
【课堂作业】:结合multiset复习 “<” 操作符重载
请修改本例,实现如果身高相同,则按姓名排序(考虑极端情况,请仍然使用multiset)。
multiset默认采用“<”比较,但也允许我们在模板实例化时,提供自定义排序时使用的比较判断式:
//Compare: 自定义的比较判断,注意:是类的名字
multiset <T, Compare>;
如果想让学生从高排到低,粗暴的做法是认为地将 Student 中的 “<” 操作符返回 “反逻辑” ,符号是“小于号”,但比较结果是 “大于” 的判断。那样太坑爹了。
为Student写一个“>”重载?不行,sets不会聪明到你写一个大于比较符重载,它就自动在内部改用大于判断,我们还是需要写一个判断式,作为第二个模板参数传入。
但注意,之前我们碰上的“判断式”,都是作为一个对象,传递给某个函数,
sets所需要的“比较判断”是模板参数,它需要一个“类名(typename)”。普通函数胜任不了,我们必须写“函数对象”:
Student类:
函数对象:
以上代码插在Student的定义之后,然后将上例中22行代码
修改为:
现在,学生可以从高排到低了。
提供包括“比较判断式类”在内的模板参数后,一个sets模板产生一个具体的sets类,那么这个类是不是只能构造出采用一模一样的排序规则的sets对象呢?不是。
sets在构造对象时,还允许我们传入具体的函数对象。比如我们让“is_taller_then”复杂一些,实现比较身高时,可以选择是否考虑姓名的因素:
接着,原023行的类型定义不变:
typedef multiset <Student, is_taller_then> SetT;
但025行构造sets对象时,传入一个特定的is_taller_then对象:
bool with_name = true;
//定制的 is_taller_then 一个对象作为入参
SetT sets(is_taller_then(with_name))