一个地图是一个元素的列表结构。每个元素都是一个键/值对,而且键是唯一的。一个地图可以按键从最小到最大进行排序。在排序中,一个键随其对应的值移动。像这样的升序排序使用了map的比较器,形式为小于()。
下面的代码在创建地图的过程中,按键以升序对地图进行排序。
map<char, int, less<char>> mp = {{'P',1}, {'N',2}, {'Q',3}, {'M',4}, {'O',5}};
for (map<char, int>::iterator ite = mp.begin(); ite != mp.end(); ite++)
cout << ite->first << " => " << ite->second << endl;
输出结果是:
M => 4
N => 2
O => 5
P => 1
Q => 3
这里的比较器的实际代码是 "less",在地图模板的特殊化中。下面的代码是在创建地图的过程中,按照键的降序对地图进行排序。
map<char, int, greater<char>> mp = {{'P',1}, {'N',2}, {'Q',3}, {'M',4}, {'O',5}};
for (map<char, int>::iterator ite = mp.begin(); ite != mp.end(); ite++)
cout << ite->first << " => " << ite->second << endl;
输出结果是:
Q => 3
P => 1
O => 5
N => 2
M => 4
这里的比较器的实际代码是map模板特殊化中的" greater"。
对于上面的代码样本,程序中包含了地图库。默认的比较器形式是less。所以,在前面的代码中可以省略掉它,以有。
map<char, int> mp
用于map模板的特殊化。
如果程序员对C++提供的比较器不满意,程序员可以自己编写。这篇文章解释了程序员如何将自己的比较器应用到map上。字符串键的正常比较是区分大小写的。使用的是不区分大小写的比较的例子:
文章内容
字符串键的正常比较
考虑一个有五个学生的班级,对于名字与分数的映射,在测试中,如下所示。
Mary => 70
John => 60
Susan => 50
Paul => 45
Joseph => 75
一个默认比较器的C++地图,less在程序中。
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main()
{
map<string, int> mp = {{"Mary",70}, {"John",60}, {"Susan",50}, {"Paul",45}, {"Joseph",75}};
for (map<string, int>::iterator ite = mp.begin(); ite != mp.end(); ite++)
cout << ite->first << " => " << ite->second << endl;
return 0;
}
输出结果是:
John => 60
Joseph => 75
Mary => 70
Paul => 45
Susan => 50
这种比较是区分大小写的。程序开始时,为cout对象加入了iostream库。接下来,map库被包含在内;然后字符串库也被包含在内。后面的语句确保程序中使用的任何名称都来自标准名称空间,除非另有说明。
在main()函数中,map被声明为初始化的。main()函数中的下一个代码段显示了排序后的结果(按键)。
自定义不区分大小写的比较运算符
C++标准库中的字符串类的小于(<)运算符是区分大小写的。如果程序员需要一个不区分大小写的字符串字面的小于运算符,他必须要有自己的运算符函数。操作符实际上是一个以特殊方式开始的函数。
从用户的角度来看,小于操作是:
left < right
其中左和右是操作数。如果左边的操作数小于右边的操作数,这个操作会产生真值。如果左边的操作数与右边的操作数相同或更大,则产生假数。该操作符用于升序排序。
一个自定义的不区分大小写的小于比较运算符,对于字符串字面来说,是。
bool operator()(char const* left, char const* right) const {
for ( ; *left != '\0' && *right != '\0'; ++left, ++right ) {
if ( tolower(*left) != tolower(*right) )
{
return ( tolower(*left) < tolower(*right) );
}
else if ( *left != *right)
{
if ( *(left+1) == '\0' && *(right+1) == '\0' )
{
return (*left < *right);
}
}
}
return (tolower(*left) < tolower(*right));
}
这里,运算符的符号不是<,而是(),它被编码为意味着<。参数left和right分别是指左和右的操作数。"char const*"表示内容字符不能被改变。在参数列表后面的 "const "意味着在地图中引用的键值(字符串)不能被改变,相对于键/值对。这并不意味着地图中每个键/值对元素的顺序不能被改变。
尽管目标小于运算符被定义为(),<仍然在代码中使用。
for-loop从最左边的字符开始,逐个字符地比较左右两个字符串字面。当两个操作数都是小写时,它检查每个操作数的第一个字符是否相同(不区分大小写)。如果它们不相同,那么如果左边的字符小于右边的字符,则返回true;否则,返回false;并且操作函数停止迭代,因为已经得到了答案。如果它们是相同的,那么就继续迭代到第二对相应的字符。
在继续比较第二对相应的字符之前,代码会检查相应的字符是否相同,但属于不同的情况;以及字符串字面的长度是否相同,并且已经到达了它们的末端。如果这些都是真的,那么",可以写成降序排序,但本文将不涉及这个问题。
比较模板的特殊化
简单来说,map的模板参数列表是:
template<class Key, class T, class Compare = less<Key>>
注意默认的比较器是less。对于上面的map,mp,以及不区分大小写的比较器,其特殊化将是。
map<const char*, int, cicomp>
其中cicomp是比较器,它是一个类型。这个类型是一个结构的名称或一个类的名称。该结构或类通常只有一个成员,也就是上面的运算符函数定义。在这个特殊化中,使用了 "const char*"而不是字符串类型。
对于结构类型,前面没有指定 "public: "的成员默认为public。对于类类型,上述操作函数必须是一个公共成员。因此,结构类型的定义将是。
struct cicomp {
bool operator()(char const* left, char const* right) const {
for ( ; *left != '\0' && *right != '\0'; ++left, ++right ) {
if ( tolower(*left) != tolower(*right) )
{
return ( tolower(*left) < tolower(*right) );
}
else if ( *left != *right)
{
if ( *(left+1) == '\0' && *(right+1) == '\0' )
{
return (*left < *right);
}
}
}
return (tolower(*left) < tolower(*right));
}
};
这两个定义的主要区别是使用了 "public: "这个指定词。类的类型定义将是。
class cicomp {
public:
bool operator()(char const* left, char const* right) const {
for ( ; *left != '\0' && *right != '\0'; ++left, ++right ) {
if ( tolower(*left) != tolower(*right) )
{
return ( tolower(*left) < tolower(*right) );
}
else if ( *left != *right)
{
if ( *(left+1) == '\0' && *(right+1) == '\0' )
{
return (*left < *right);
}
}
}
return (tolower(*left) < tolower(*right));
}
};
这个比较器的程序应该是这样开始的。
#include <&iostreamgt;
#include <&mapgt;
#include <&cctypegt;
using namespace std;
cctype库是用于tolower()函数的。
自定义比较器的主函数
下面的C++ main()函数适用于结构类型或类类型。
int main()
{
map<const char*, int, cicomp> mp = {{"Mary",70}, {"John",60}, {"Susan",50}, {"Paul",45}, {"Joseph",75}};
for (map<const char*, int, cicomp>::iterator ite = mp.begin(); ite != mp.end(); ite++)
cout << ite->first << " => " << ite->second << endl;
return 0;
}
输出结果是:
John => 60
Joseph => 75
Mary => 70
Paul => 45
Susan => 50
为不区分大小写的字符串字面比较器。注意,迭代器的模板特殊化与地图的模板特殊化是一样的。
结论
地图排序的默认比较器是less。比较器是一个类型,它的名字是map声明的第三个参数,属于map模板专业化。这个类型可以是一个结构体或一个类,它的唯一成员可能是一个自定义运算符的定义。本文已经展示了自定义小于运算符的情况。其他的比较运算符也可以同样被定义。