C++ STL关联式容器自定义排序规则的2种方法

前面在讲解如何创建 map、multimap、set 以及 multiset 容器时,遗留了一个问题,即如何自定义关联式容器中的排序规则?
实际上,为关联式容器自定义排序规则的方法,已经在 《STL priority_queue自定义排序方法》一节中做了详细的讲解。换句话说,为 Priority_queue 容器适配器自定义排序规则的方法,同样适用于所有关联式容器。

总的来说,为关联式容器自定义排序规则,有以下 2 种方法。

1) 使用函数对象自定义排序规则

在掌握此方法之前,读者必须对函数对象有基本的了解,可阅读《C++函数对象》一节。

无论关联式容器中存储的是基础类型(如 int、double、float 等)数据,还是自定义的结构体变量或类对象(包括 string 类),都可以使用函数对象的方式为该容器自定义排序规则。

下面样例以 set 容器为例,演示了如何用函数对象的方式自定义排序规则:

#include <iostream>
#include <set>  // set
#include <string>  // string
using namespace std;
//定义函数对象类
class cmp {
public:
 //重载 () 运算符
 bool operator ()(const string &a,const string &b) {
  //按照字符串的长度,做升序排序(即存储的字符串从短到长)
  return (a.length() < b.length());
 }
};
int main() {
 //创建 set 容器,并使用自定义的 cmp 排序规则
 std::set<string, cmp>myset{"http://jb51.net.net/stl/",
        "http://jb51.net.net/python/",
        "http://jb51.net.net/java/"};
 //输出容器中存储的元素
 for (auto iter = myset.begin(); iter != myset.end(); ++iter) {
   cout << *iter << endl;
 }
 return 0;
}

程序执行结果为:
http://jb51.net.net/stl/
http://jb51.net.net/java/
http://jb51.net.net/python/

重点分析一下 6~13 行代码,其定义了一个函数对象类,并在其重载 () 运算符的方法中自定义了新的排序规则,即按照字符串的长度做升序排序。在此基础上,程序第 17 行代码中,通过将函数对象类的类名 cmp 通过 set 类模板的第 2 个参数传递给 myset 容器,该容器内部排序数据的规则,就改为了以字符串的长度为标准做升序排序。
需要注意的是,此程序中创建的 myset 容器,由于是以字符串的长度为准进行排序,因此其无法存储相同长度的多个字符串。

另外,C++ 中的 struct 和 class 非常类似(有关两者区别,可阅读《C++ struct和class到底有什么区别》一文),前者也可以包含成员变量和成员函数。因此上面程序中,函数对象类 cmp 也可以使用 struct 关键字创建:

//定义函数对象类
struct cmp {
 //重载 () 运算符
 bool operator ()(const string &a, const string &b) {
  //按照字符串的长度,做升序排序(即存储的字符串从短到长)
  return (a.length() < b.length());
 }
};

值得一提的是,在定义函数对象类时,也可以将其定义为模板类。比如:

//定义函数对象模板类
template <typename T>
class cmp {
public:
 //重载 () 运算符
 bool operator ()(const T &a, const T &b) {
  //按照值的大小,做升序排序
  return a < b;
 }
};

注意,此方式必须保证 T 类型元素可以直接使用关系运算符(比如这里的 < 运算符)做比较。

2) 重载关系运算符实现自定义排序

其实在 STL 标准库中,本就包含几个可供关联式容器使用的排序规则,如表 1 表示。

表 1 C++ STL标准库适用于关联式容器的排序规则

排序规则 功能
std::less<T>    底层采用 < 运算符实现升序排序,各关联式容器默认采用的排序规则。
std::greater<T> 底层采用 > 运算符实现降序排序,同样适用于各个关联式容器。
std::less_equal<T> 底层采用 <= 运算符实现升序排序,多用于 multimap 和 multiset 容器。
std::greater_equal<T> 底层采用 >= 运算符实现降序排序,多用于 multimap 和 multiset 容器。

值得一提的是,表 1 中的这些排序规则,其底层也是采用函数对象的方式实现的。以 std::less<T> 为例,其底层实现为:

template <typename T>
struct less {
 //定义新的排序规则
 bool operator()(const T &_lhs, const T &_rhs) const {
  return _lhs < _rhs;
 }
}

在此基础上,当关联式容器中存储的数据类型为自定义的结构体变量或者类对象时,通过对现有排序规则中所用的关系运算符进行重载,也能实现自定义排序规则的目的。

注意,当关联式容器中存储的元素类型为结构体指针变量或者类的指针对象时,只能使用函数对象的方式自定义排序规则,此方法不再适用。

举个例子:

#include <iostream>
#include <set>  // set
#include <string>  // string
using namespace std;
//自定义类
class myString {
public:
 //定义构造函数,向 myset 容器中添加元素时会用到
 myString(string tempStr) :str(tempStr) {};
 //获取 str 私有对象,由于会被私有对象调用,因此该成员方法也必须为 const 类型
 string getStr() const;
private:
 string str;
};
string myString::getStr() const{
 return this->str;
}
//重载 < 运算符,参数必须都为 const 类型
bool operator <(const myString &stra, const myString & strb) {
 //以字符串的长度为标准比较大小
 return stra.getStr().length() < strb.getStr().length();
}
int main() {
 //创建空 set 容器,仍使用默认的 less<T> 排序规则
 std::set<myString>myset;
 //向 set 容器添加元素,这里会调用 myString 类的构造函数
 myset.emplace("http://jb51.net.net/stl/");
 myset.emplace("http://jb51.net.net/c/");
 myset.emplace("http://jb51.net.net/python/");
 //
 for (auto iter = myset.begin(); iter != myset.end(); ++iter) {
  myString mystr = *iter;
  cout << mystr.getStr() << endl;
 }
 return 0;
}

程序执行结果为:
http://jb51.net.net/c/
http://jb51.net.net/stl/
http://jb51.net.net/python/

在这个程序中,虽然 myset 容器表面仍采用默认的 std::less<T> 排序规则,但由于我们对其所用的 < 运算符进行了重载,使得 myset 容器内部实则是以字符串的长度为基准,对各个 mystring 类对象进行排序。

另外,上面程序以全局函数的形式实现对 < 运算符的重载,还可以使用成员函数或者友元函数的形式实现。其中,当以成员函数的方式重载 < 运算符时,该成员函数必须声明为 const 类型,且参数也必须为 const 类型:

bool operator <(const myString & tempStr) const {
 //以字符串的长度为标准比较大小
 return this->str.length() < tempStr.str.length();
}

至于参数的传值方式是采用按引用传递还是按值传递,都可以(建议采用按引用传递,效率更高)。

同样,如果以友元函数的方式重载 < 运算符时,要求参数必须使用 const 修饰:

//类中友元函数的定义
friend bool operator <(const myString &a, const myString &b);
//类外部友元函数的具体实现
bool operator <(const myString &stra, const myString &strb) {
 //以字符串的长度为标准比较大小
 return stra.str.length() < strb.str.length();
}

当然,本节所讲自定义排序规则的方法并不仅仅适用于 set 容器,其它关联式容器(map、multimap、multiset)也同样适用,有兴趣的读者可自行编写代码验证。

到此这篇关于C++ STL关联式容器自定义排序规则的2种方法的文章就介绍到这了,更多相关C++ STL关联式容器自定义排序内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++ STL priority_queue自定义排序实现方法详解

    前面讲解 priority_queue 容器适配器时,还遗留一个问题,即当 <function> 头文件提供的排序方式(std::less<T> 和 std::greater<T>)不再适用时,如何自定义一个满足需求的排序规则. 首先,无论 priority_queue 中存储的是基础数据类型(int.double 等),还是 string 类对象或者自定义的类对象,都可以使用函数对象的方式自定义排序规则.例如: #include<iostream> #in

  • C++ STL关联式容器自定义排序规则的2种方法

    前面在讲解如何创建 map.multimap.set 以及 multiset 容器时,遗留了一个问题,即如何自定义关联式容器中的排序规则? 实际上,为关联式容器自定义排序规则的方法,已经在 <STL priority_queue自定义排序方法>一节中做了详细的讲解.换句话说,为 Priority_queue 容器适配器自定义排序规则的方法,同样适用于所有关联式容器. 总的来说,为关联式容器自定义排序规则,有以下 2 种方法. 1) 使用函数对象自定义排序规则 在掌握此方法之前,读者必须对函数对

  • C++ STL 序列式容器与配接器的简单使用

    目录 容器概述 序列式容器 array vector list deque forward_list Adapter(配接器) stack queue priority_queue 容器概述 C++标准里提供了以下容器或容器配接器: 序列式容器 配接器 关联式容器 不定序关联容器 array stack set unordered_set vector queue map unordered_map list priority_queue multiset unordered_multiset

  • Android自定义荷载进度的两种方法

    本文将用两个方法来写类似汽车荷载的进度 用LinearLayout的addview方法加上for循环 用自定义控件的方法 先上截图 1. 用LinearLayout的addview方法加上for循环 1.1 processtest01.xml文件: <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/a

  • Android中自定义标题栏样式的两种方法

    原装的Android标题栏配色比较单调,就是黑色的一坨,现在假设你的软件需要独自添加标题栏,这样不仅美观而且可以将进度条等加进去,如何实现: 方法一.在你的那张Activity中onCreate方法中加上下面代码: requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); setContentView(R.layout.main); //软件activity的布局 getWindow().setFeatureInt(Window.FEATURE_CUS

  • 详解python中自定义超时异常的几种方法

    最近在项目中调用第三方接口时候,经常会出现请求超时的情况,或者参数的问题导致调用异代码异常.针对超时异常,查询了python 相关文档,没有并发现完善的包来根据用户自定义的时间来抛出超时异常的模块.所以自己干脆自己来实现一个自定义的超时异常.目前找到了两种方式来实现超时异常的功能(signal.alarm().threading实现超时异常) 方法1 thread + time 原理:将要调用的功能函数放入子线程,通过设定子线程的阻塞时间,超时则主线程并不会等待子线程的执行.主线程退出,子线程就

  • 利用div+jquery自定义滚动条样式的2种方法

    最近做项目中有一个模块是用于实时监控的,左边有个菜单栏用于显示所有的设备,那当然是从数据库中动态获取的了,右边是个iframe用于显示监控画面.本来这个功能并不复杂,左边的菜单项是利用dtree.js来实现的,可时当功能实现完成之后,却发现一个问题,就是左边菜单栏中的设备名有的会很长,会超出了div的长度,准确说是左边iframe的宽度和长度不够.那么,这时就必须要利用滚动条了,可以设置左边菜单项div的overflow-x:auto;overlfow-y:auto;这样就会自动生成了滚动条,但

  • Jquery自定义button按钮的几种方法

    1.第一种方法比较简单 复制代码 代码如下: "columns": [ { "data": null,defaultContent: '<input type="button" onclick="" value="呵呵" />', orderable: false }, ], 从代码字面意思就能明白什么意思,但是怎样从里边获取改行的信息我还不知道,所以显得不是太实用,定义默认值还可以. 复制代码

  • 关于STL中set容器的一些总结

    1.关于set C++ STL 之所以得到广泛的赞誉,也被很多人使用,不只是提供了像vector, string, list等方便的容器,更重要的是STL封装了许多复杂的数据结构算法和大量常用数据结构操作.vector封装数组,list封装了链表,map和set封装了二叉树等,在封装这些数据结构的时候,STL按照程序员的使用习惯,以成员函数方式提供的常用操作,如:插入.排序.删除.查找等.让用户在STL使用过程中,并不会感到陌生. 关于set,必须说明的是set关联式容器.set作为一个容器也是

  • Java针对ArrayList自定义排序的2种实现方法

    本文实例讲述了Java针对ArrayList自定义排序的2种实现方法.分享给大家供大家参考,具体如下: Java中实现对list的自定义排序主要通过两种方式 1)让需要进行排序的对象的类实现Comparable接口,重写compareTo(T o)方法,在其中定义排序规则,那么就可以直接调用Collections.sort()来排序对象数组 public class Student implements Comparable{ private int id; private int age; p

随机推荐