IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> c/c++中有序容器、排序函数在应用用户自定义数据类型时比较函数设计 -> 正文阅读

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

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

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

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

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

??????? 有序容器、排序函数对类型的比较,无非就是满足比较运算数(==、!=、>、<、>=、<=),因此需要类型提供相关的运算符实现就能达到要求。例如,为了实现快速查找,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。

??????? 那么在实际项目应用,我们常遇到复合结构数据类型作为有序容器的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_); 
}

三、测试

???????? 下来,创建一个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;  
}

本文编译环境是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)

????????测试输出结果:

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-09-30 00:33:07  更:2022-09-30 00:33:44 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 4:01:46-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码