STL工具库使用解析系列之二:自定义比较函数的两种方式(重载和仿函数)

来源:互联网 发布:通过itunes安装软件 编辑:程序博客网 时间:2024/06/05 15:32

C++比C不仅多了面向对象支持class类编程,还支持泛型编程,以及提供丰富的STL开发工具库。泛型是一种在编译期间动态具体化的技术,使得一些通用算法或数据结构的封装变得十分方便。在C++标准中指出,当一个模板不被使用时,它就不应该被具体化。对于模板在编译期间如何特化,其实用到的概念诸多,比如“惰性求值”和“模式匹配”,两点关键:
1.对于函数模板: 确定特化的参数类型是隐式传进去的,编译器将根据传入值得类型来推到模板参数的类型,函数模板的参数不能有默认值;
2.对于类模板:使用该类模板时必须显式传入参数类型,如前面提到过的queue.h中提供的队列类模板,具体使用时,应该诸如 _tool::queue emptyList;

自定义比较函数有两种方式:1.结构体或类直接提供比较符重载;2.仿函数

结构体直接提供重载,这种方式最为简便,但是限制较多:1.结构体必须是自己提供定义的,若是采用没有修改权限的结构体,则显然无法在结构体内部定义符合我们预期要求的重载比较符;2.结构体的比较方式固定了,若是结构体含有诸多元素,而不同场景中,作为主键进行排序的元素不同,则这时显然就不能在结构体写死了比较符的逻辑。

对于结构体直接提供重载不方便的场景,则只能使用仿函数机制。仿函数(something that performs a function),即其行为类似函数。C++中的仿函数是通过在类中重载()运算符实现的,而仿函数之所以能生效,还是因为STL中关于map\set等数据结构都是提供了模板类接口,其中若是需要提供排序的数据结构,则其特化接口中是存在如下的形参的
class _Compare __STL_DEPENDENT_DEFAULT_TMPL(less<_Key>
该形参的意义是指该数据结构采用默认的less的模板比较函数,这时只需要将我们定义了仿函数的结构体或类取代less的默认赋值便可以启动我们自定义的比较函数了。

可以看下STL源码关于map和set集合的声明:

// map 和 set 底层存储结构式都是红黑树// Forward declarations of operators == and <, needed for friend declarations.template <class _Key, class _Tp,  //分别是键类型,值类型          class _Compare __STL_DEPENDENT_DEFAULT_TMPL(less<_Key>), //排序的特化类型,默认是less<>          class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) > //配置器class map;template <class _Key,       class _Compare __STL_DEPENDENT_DEFAULT_TMPL(less<_Key>),      class _Alloc = __STL_DEFAULT_ALLOCATOR(_Key) >class set;

来看下测试代码便可以快速地了解情况了

#include <map>#include <string>#include <iostream>using namespace std;typedef struct studentGradeAndNameInfo{    int     grade;    string  studentName;    bool operator < (const studentGradeAndNameInfo & A) const    {        if (grade < A.grade) return true;        if (grade == A.grade)            return studentName.compare(A.studentName) < 0;        return false;    }}StudentInfo, *PStudentInfo;typedef struct FoodAndPrice{    int     price;    string  vegetables;}FoodInfo;class sort{    public:        bool operator() (FoodInfo const & A, FoodInfo const & B) const        {            if (A.price < B.price)                return true;            if (A.price == B.price)                return A.vegetables.compare(B.vegetables) < 0;            return false;        }};int main(){    /*map的核心还是通过红黑树来排序key值,从而实现高效的检索和管理操作,而STL默认是采用升序的,但是这也是建立在key的数据类型支持排序    *在本程序中,mapStudent 为map<int, string>,其key值为int类型,故而可以顺畅地完成排序,可如果key值是一个用户自定义的数据类型呢?    *比如用户自定义了一个结构体ownStruct,这时显然用户在提供自定义结构体时还需要提供该结构体的比较算符"<"重载 或者提供仿函数    */    map<StudentInfo, int> studentGrade;    StudentInfo info = {90, "peter"};    studentGrade.insert(map<StudentInfo, int>::value_type(info, 2010110111));    info.grade = 95; info.studentName = "kaisen";    studentGrade.insert(map<StudentInfo, int>::value_type(info, 2010110112));    info.grade = 90; info.studentName = "pierce";    studentGrade.insert(map<StudentInfo, int>::value_type(info, 2010110113));    info.grade = 90; info.studentName = "smith";    studentGrade.insert(map<StudentInfo, int>::value_type(info, 2010110114));    info.grade = 79; info.studentName = "black";    studentGrade.insert(map<StudentInfo, int>::value_type(info, 2010110115));    map<StudentInfo, int>::iterator newIter;    for (newIter = studentGrade.begin(); newIter!=studentGrade.end(); newIter++)        cout<<newIter->first.grade<<"  "<<newIter->first.studentName<<"  "<<newIter->second<<endl;    cout<<endl<<endl;    /*如果结构体并没有提供有效的比较符重载,那么便需要提供仿函数,用以介入相应结构体的比较过程*/    /*看下STL源码中对于map的声明    template <class _Key, class _Tp,          class _Compare __STL_DEPENDENT_DEFAULT_TMPL(less<_Key>),          class _Alloc = __STL_DEFAULT_ALLOCATOR(_Tp) >    class map;    */    //要是使用自定义的比较函数,使用仿函数时,必须将第三个比较类采用的仿函数类型手动传入,这里是手动传入sort    map<FoodInfo, int, sort> market;    FoodInfo food = {90, "pado"};    market.insert(map<FoodInfo, int>::value_type(food, 133));    food.price = 10; food.vegetables = "tomato";    market.insert(map<FoodInfo, int>::value_type(food, 12));    food.price = 10; food.vegetables = "tofu";    market.insert(map<FoodInfo, int>::value_type(food, 37));    food.price = 40; food.vegetables = "mulk";    market.insert(map<FoodInfo, int>::value_type(food, 71));    map<FoodInfo, int, sort>::iterator foodIter;    for (foodIter = market.begin(); foodIter != market.end(); foodIter++)        cout<<foodIter->first.price<<" "<<foodIter->first.vegetables<<" "<<foodIter->second<<endl;}   

1. 采用结构体内部重载方式的结果
这里写图片描述
2. 采用仿函数自定义比较函数的结果
这里写图片描述
通过运行结果,可以发现无论是结构体重载比较符还是通过仿函数都可以得到预期的结果,即最后map内部的数据类型是按照key的升序排列的。