c/c++中有序容器、排序函数在应用用户自定义数据类型时比较函数设计

目录

一、有序容器、排序函数的对于数据类型的排序要求

二、有序容器、排序函数对类型的比较能力设计

        2.1 容器排序的关系操作符要求

        2.2 自定义类类型关系操作符设计

三、测试

         3.1 测试代码设计

        3.2 编译及演示


一、有序容器、排序函数的对于数据类型的排序要求

        在使用有序容器 map/multimap 和 set/multiset等时,如果是采用常用数据类型(如int、float)作为其key,是能够很好实现的(要么可直接比对,要么标准库已实现),但采用自定义类型结构时,就需要自行提供该类型的比较能力,容器才能基于该类型的key进行有效排序。

        同样的对于排序函数qsort, qsort_s,搜索函数bsearch, bsearch_s等API,用户自定义类型时,也需要自行提供该类型的比较能力。

二、有序容器、排序函数对类型的比较能力设计

        2.1 容器排序的关系操作符要求

        有序容器、排序函数对类型的比较,无非就是满足比较运算数(==、!=、>、<、>=、<=),因此需要类型提供相关的运算符实现就能达到要求。例如,为了实现快速查找,map内部本身就是按序存储的(比如红黑树)。在我们插入<key, value>键值对时,就会按照key的大小顺序进行存储。这是作为key的类型必须能够进行<运算比较的原因。如果string类型作为key,就是按字典顺序规则排序储存的。

        map是stl里面的一个模板类,map的定义:

template < class Key, class T, class Compare = less<Key>,
           class Allocator = allocator<pair<const Key,T> > > class map; 

        第三个参数: class Compare = less<Key> 这是一个class类型的,而且提供了默认值 less<Key>。 less是stl里面的一个函数对象,即调用操作符的类,其对象常称为函数对象(function object),它们是行为类似函数的对象。表现出一个函数的特征,就是通过“对象名+(参数列表)”的方式使用一个 类,其实质是对operator()操作符的重载。less的实现如下:

template <class T> struct less : binary_function <T,T,bool> 
{
    bool operator() (const T& x, const T& y) const {return x<y;}
};

        map指定了less作为其默认比较函数(对象),所以通常如果不自己指定Compare,map中键值对就会按照Key的less顺序进行组织存储,例如string的key,及int的value一个map,就是按照字典顺序输出的,即string的less序列,其真实定义是map<string, int, less<string> > name_less_map。类似地,库还提供与less相对的还有greater,由于其不是默认方式,就缺失需要需要使用时,要显式定义map<string, int, greater<string> > name_more_map。

        2.2 自定义类类型关系操作符设计

        那么在实际项目应用,我们常遇到复合结构数据类型作为有序容器的key时,其实现基于以上原理,类似的提供该数据结构的比较函数(对象)即可。假设,现有一个类型,包含两个int型变量,组ID及元ID:

class KeyObj
{
public:
	KeyObj(int _gid, int _id)
		: m_gid(_gid), m_id(_id)
	{
	};
private:
	int m_gid;
	int	m_id;
};

        以该KeyObje类型的作为有序容器的key,就需要实现其比较运算数的操作函数:

inline bool operator==(const KeyObj& obj1, const KeyObj& obj2);
inline bool operator!=(const KeyObj& obj1, const KeyObj& obj2);
inline bool operator>=(const KeyObj& obj1, const KeyObj& obj2);
inline bool operator<=(const KeyObj& obj1, const KeyObj& obj2);
inline bool operator>(const KeyObj& obj1, const KeyObj& obj2);
inline bool operator<(const KeyObj& obj1, const KeyObj& obj2);

        而运算操作符的实现就是KeyObj内部比较逻辑的设计了,假如我们明确着这样的规则,如组ID优先于元ID:

class KeyObj
{
public:
	KeyObj(int _gid, int _id)
		: m_gid(_gid), m_id(_id)
	{
	};
	//
	static int cmp_Key(const KeyObj &obj1, const KeyObj &obj2)
	{
		int diff = obj1.m_gid - obj2.m_gid;    //组ID优先于元ID
		if (diff != 0) 		return diff;
		diff = obj1.m_id - obj2.m_id;          //在组ID一致时,元ID才有比较意义
		if (diff != 0) 		return diff;
		return 0;
	};
    int get_gid() const {return m_gid; };
	int get_id() const { return m_id; };
private:
	int m_gid;
	int	m_id;
};
inline bool operator==(const KeyObj& obj1, const KeyObj& obj2) { return KeyObj::cmp_Key(obj1, obj2) == 0; }
inline bool operator!=(const KeyObj& obj1, const KeyObj& obj2) { return KeyObj::cmp_Key(obj1, obj2) != 0; }
inline bool operator>=(const KeyObj& obj1, const KeyObj& obj2) { return KeyObj::cmp_Key(obj1, obj2) >= 0; }
inline bool operator<=(const KeyObj& obj1, const KeyObj& obj2) { return KeyObj::cmp_Key(obj1, obj2) <= 0; }
inline bool operator>(const KeyObj& obj1, const KeyObj& obj2) { return KeyObj::cmp_Key(obj1, obj2) > 0; }
inline bool operator<(const KeyObj& obj1, const KeyObj& obj2) { return KeyObj::cmp_Key(obj1, obj2) < 0; }

        对于qsort, qsort_s来说,


定义于头文件 <stdlib.h>
   
void qsort( void *ptr, size_t count, size_t size,int (*comp)(const void *, const void *) ); 

其中参数comp - 比较函数。若首个参数小于第二个,则返回负整数值,若首个参数大于第二个,则返回正整数值,若两参数等价,则返回零。

比较函数的签名应等价于如下形式:

 int cmp(const void *a, const void *b);

该函数必须不修改传递给它的对象,而且在调用比较相同对象时必须返回一致的结果,无关乎它们在数组中的位置。
 

        因此需要再做适应该比较函数签名模式进行调整:

inline int cmp_Key_p(const void* obj1, const void* obj2) 
{ 
	const KeyObj obj1_= *(const KeyObj*)obj1;
	const KeyObj obj2_= *(const KeyObj*)obj2;
	return KeyObj::cmp_Key(obj1_, obj2_); 
}

三、测试

         3.1 测试代码设计

        下来,创建一个KeyObj数组,并将这些元素作为key插入到map容器,并调用qsort函数对KeyObj数组进行排序,打印排序结果和map容器顺序输出结果。本文集中在一个test.cpp源文件中实现:

#include <stdio.h>  
#include <stdlib.h>  
#include <iostream>
#include <map>

class KeyObj
{
public:
	KeyObj(int _gid, int _id)
		: m_gid(_gid), m_id(_id)
	{
	};
	//
	static int cmp_Key(const KeyObj &obj1, const KeyObj &obj2)
	{
		int diff = obj1.m_gid - obj2.m_gid;
		if (diff != 0) 		return diff;
		diff = obj1.m_id - obj2.m_id;
		if (diff != 0) 		return diff;
		return 0;
	};
	int get_gid() const {return m_gid; };
	int get_id() const { return m_id; };
private:
	int m_gid;
	int	m_id;
};
inline int cmp_Key_p(const void* obj1, const void* obj2) 
{ 
	const KeyObj obj1_= *(const KeyObj*)obj1;
	const KeyObj obj2_= *(const KeyObj*)obj2;
	return KeyObj::cmp_Key(obj1_, obj2_); 
}
//
inline bool operator==(const KeyObj& obj1, const KeyObj& obj2) { return KeyObj::cmp_Key(obj1, obj2) == 0; }
inline bool operator!=(const KeyObj& obj1, const KeyObj& obj2) { return KeyObj::cmp_Key(obj1, obj2) != 0; }
inline bool operator>=(const KeyObj& obj1, const KeyObj& obj2) { return KeyObj::cmp_Key(obj1, obj2) >= 0; }
inline bool operator<=(const KeyObj& obj1, const KeyObj& obj2) { return KeyObj::cmp_Key(obj1, obj2) <= 0; }
inline bool operator>(const KeyObj& obj1, const KeyObj& obj2) { return KeyObj::cmp_Key(obj1, obj2) > 0; }
inline bool operator<(const KeyObj& obj1, const KeyObj& obj2) { return KeyObj::cmp_Key(obj1, obj2) < 0; }

//程序入口
int main(void){  
	KeyObj ks[5] = {KeyObj(3,1),KeyObj(2,4),KeyObj(1,5),KeyObj(2,2),KeyObj(1,3)};
	std::map<KeyObj,int> ms;
	for(int i=0; i<5; i++)
	{
		ms[ks[i]] = i;
	}
	qsort(ks, 5, sizeof(KeyObj), cmp_Key_p);//数组排序
	for(int i=0; i<5; i++)
	{
		std::cout << "[" <<i<< "]" <<ks[i].get_gid()<< "," << ks[i].get_id() << std::endl;
	}
	std::map<KeyObj,int>::iterator  iter;
	int i=0;
	for(iter=ms.begin(); iter!=ms.end(); iter++)
	{
		std::cout << "[" <<i++<< "]" << iter->first.get_gid() << "," <<iter->first.get_id() 
			<<":"<< iter->second << std::endl;
	}
    //KeyObj key(1,5);
    //KeyObj *res = bsearch(&key, ks, 5, sizeof KeyObj, cmp_Key_p);//搜索函数调用测试

    system("read");  
    return 0;  
}

        3.2 编译及演示

        本文编译环境是64bit的centos7下gcc 4.8.2编译,Makefile如下

#/bin/sh
#CX= D:/workForSoftware/MinGW/bin/g++.exe
CX= g++

BIN 		:= ./bin
TARGET      := test
FLAGS		:= -std=c++11 
#-DNDEBUG
#-static 
SRCDIR 		:= ./src
#INCLUDES
INCLUDEDIR 	:= -I"$(SRCDIR)"

source		:= $(wildcard $(SRCDIR)/*.cpp $(SRCDIR)/*.c)

$(TARGET) :
	$(CX) $(FLAGS) $(INCLUDEDIR) $(source)  -o $(BIN)/$(TARGET)

clean:
	rm  $(BIN)/$(TARGET)

        测试输出结果:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

py_free-物联智能

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值