到目前为止,我们已经把有序关联式容器全部学习完了,读者是否能够完整地枚举它们并清晰地区分它们呢?它们分别是:map容器、multimap容器、set容器和multiset容器。它们都有一个共同的特点,就是能够在元素插入时按照默认的排序规则将键(key)排序(默认都是升序排序),那么如何更改有序关联式容器的排序规则呢?对于这个问题,我推荐使用仿函数来实现。什么是仿函数,读者心里可能一头雾水。简单来说,仿函数就是把对象当函数使用,其实也没有什么深奥的,基本知识就是充分利用对象和operator'()'重载这个概念。下面我将通过代码的方式为读者详细介绍该方法:

1. 什么是仿函数?(基础)

把对象当函数使用,本质是重载对象内‘()’函数调用运算符,调用方式与调用函数无异,看起来就像对象在模仿函数”行为“,故称作”仿函数“。

#include <iostream>
using namespace std;
/*仿函数*/ 
/*充分利用struct的public特性*/ 
struct bigger
{
bool operator()(int a,int b)const 
{
return a>b;
}
};
void test()
{
bigger bgr;
if(bgr(1,0))cout << "我们通过仿函数实现1>0的比较\n";
}
int main() {
    test();
    return 0;
}

编译结果如下:

仿函数

通过对象bigger我们调用'()'实现比较功能,成功实现1>0这个判断。

2. 在学会仿函数的基础下,读者要知道这么一个库——<functional>,它被称为STL库里面的”函数对象头文件“,通过调用里面的模板对象实现仿函数的功能,我们也可以把它视为”模板化的仿函数“。(扩展)

#include <iostream>
#include<functional> 
using namespace std;
/*学习函数对象库*/ 
void test()
{
greater<int> gtr;
less<int> les;
if(gtr(1,0))cout << "通过对象greater实现比较行为:" << "\t1>0\n";
if(les(0,1))cout << "通过对象less实现比较行为:"<< "\t0<1\n" ;
}
int main() {
    test();
    return 0;
}

编译结果如下:

<functional>

学习使用这个库能够提高编程效率,读者需要走出舒适圈,适应这个函数对象库。

3. 有了仿函数这个概念,我们就可以通过仿函数更改有序关联式容器的默认排序规则。值得注意的是,自定义排序是容器的一部分属性,需要在容器创建之初就指定排序规则,如果容器创建后再”制定规则“则无法成功更改。(实操)

#include <iostream>
#include<functional> 
#include<map>
#include<set> 
#include<utility>
using namespace std;
/*通过仿函数更改有序关联式容器的默认排序规则*/ 
struct cmp
{
bool operator()(int a,int b)
{
return a>b;
}
};
void test()
{
/*创建之初就要指定排序规则*/ 
/*既可以通过仿函数又可以通过functional库*/ 
/*map*/
map<int,int,cmp> mp{{1,1},{2,2},{3,3}};
cout << "遍历map容器: " ;
for(auto it = mp.begin();it!=mp.end();++it) cout << "key="<< it->first << "&&value=" << it->second << " ";
cout << '\n';
/*multimap*/
multimap<int,int,cmp> mmp{{1,1},{2,2},{3,3}};
cout << "遍历multimap容器: " ;
for(auto it = mmp.begin();it!=mmp.end();++it) cout << "key="<< it->first << "&&value=" << it->second << " ";
cout << '\n';
/*set*/
set<int,greater<int>> s{1,2,3};
cout << "遍历set容器: " ;
for(auto it = s.begin();it!=s.end();++it) cout << *it << " ";
cout << '\n';
/*multiset*/
multiset<int,greater<int>> ms{1,2,3};
cout << "遍历multiset容器: " ;
for(auto it = ms.begin();it!=ms.end();++it) cout << *it << " ";
}
int main() {
    test();
    return 0;
}

编译结果如下:

仿函数实现有序关联式容器自定义排序

我们分别通过cmp和greater实现有序关联式容器逆序排序,建议读者使用通用的<functional>对象,提高代码的可读性。

4. 当然,在使用有序关联式容器时,我们也可能遇到键值(key)为自定义数据类型,这时候需要我们重载比较运算符'<'实现自定义排序规则,切记有序关联式容器内默认是重载'<'而非'>',不要重载其他比较运算符。(进阶)

#include <iostream>
#include<functional> 
#include<map>
#include<set> 
#include<utility>
using namespace std;
/*如果是自定义数据类型,则可通过重载'<'更改有序关联式容器默认的排序规则*/ 
/*假如自定义数据类型是一个坐标,有坐标dire和步数step*/ 
struct node//public特性 
{
bool operator<(const node&n)const 
{
return m_step<n.m_step;
}
node(int step,pair<int,int> dire): m_step(step),m_dire(dire){};
int m_step;//步数 
pair<int,int> m_dire;//坐标 
};
void test()
{
/*以map为例,其他multimap、set、multiset都是千篇一律的 */
map<node,int> mp{{{1,{1,1}},1},{{2,{2,2}},2},{{3,{3,3}},3}} ;
cout << "遍历容器为:\n"; 
for(auto it = mp.begin();it!=mp.end();++it)
{
cout <<"key: " <<  "step=" << it->first.m_step << ",&&《" << it->first.m_dire.first<< ","  << it->first.m_dire.second << "》 "<< "|value: " <<  it->second <<'\n'; 
}
cout << '\n' ;
}
int main() {
    test();
    return 0;
}

编译结果如下:

自定义数据类型重载'<'

当你想要逆序排序时,仅需在node里面更改排序规则即可!具体操作为将“return m_step<n.m_step;”改为“return m_step>n.m_step;”。

至此,你就能随心所欲地更改有序关联式容器的排序规则。

总结:本节主要讨论了如何更改有序关联式容器的排序规则,主要是通过仿函数进行操作,如果是自定义数据类型,则需重载对象内的operator'<'。

点赞(0)

C语言网提供由在职研发工程师或ACM蓝桥杯竞赛优秀选手录制的视频教程,并配有习题和答疑,点击了解:

一点编程也不会写的:零基础C语言学练课程

解决困扰你多年的C语言疑难杂症特性的C语言进阶课程

从零到写出一个爬虫的Python编程课程

只会语法写不出代码?手把手带你写100个编程真题的编程百练课程

信息学奥赛或C++选手的 必学C++课程

蓝桥杯ACM、信息学奥赛的必学课程:算法竞赛课入门课程

手把手讲解近五年真题的蓝桥杯辅导课程

Dotcpp在线编译      (登录可减少运行等待时间)