写在之前:
众所周知,vector是STL中很好用的一个类,我们可以通过vector实现很多操作,今天就来说一说利用vector如何实现查询操作
回顾一下------
》基础数据类型查询:find()
一般的,对于c++内置的基础数据类型,我们可以直接通过 find() 函数( #include < algorithm >)来进行查询.
函数原型:
返回值:vector迭代器 find(迭代起点,迭代终点,要查询的值(基础数据类型));
std::vector< VALUE_TYPE >::iterator it = find_if(vec.begin(),vec.end(),VALUE);
函数实例:
vector<int> intVec;
intVec.push_back(1);
intVec.push_back(2);
intVec.push_back(4);
intvec.push_back(5);
vector<int>::iterator it = find( intVec.begin( ), intVec.end( ), 5 );
if ( it != intVec.end( ) ) cout<<"true";
else cout<<"false";
回顾完基础数据类型的查找之后,我们进入
》》》》》》正题!!!
》类/结构体自定义查询:find_if()
在实际的编程过程中,基础数据类型的查询还是少数,更多的情况下我们需要为自己定义的结构体或者类进行排序、查询操作
先不急着看函数原型,来一个情景代入:
为了存储学生的成绩信息,我创建了一个结构体:stuInfo ,里面存放有 学生ID,四科成绩,四科排名等等信息
typedef struct{
string id;
int score[4];
int rank[4]={-1};
}stuInfo;
但现在,数据存好了,但是我希望能够通过 idVec[m] 中存放的几个想要查询成绩的 学生的ID,来为他们查找结构体中各自的成绩和排名信息
那么这个时候,find_if() 函数就能派上用场
函数原型:
返回值:vector迭代器 find_if(迭代起点,迭代终点,一元函数);
std::vector< STRUCT >::iterator it = find_if(vec.begin(),vec.end(),COMP_FUNCTION);
这里的最后一个参数,要求的是unary function,在此即是只有一个参数,并且返回值为bool类型的函数,而且数据类型是vector容器中放入的数据类型,在这里,参数类型即为学生信息结构体:stuInfo
首先我们很容易可以实现的是类似于sort函数那样的,写一个比较函数作为参数填入:
bool compare(stuInfo si){
string id="1010134";
return (si.id.compare(id)==0);
}
std::vector<stuInfo>::iterator it = find_if(info.begin(),info.end(),compare);
但是!!!这种方式我们明显就只能查找ID为1010134的这位同学的信息,那怎么做到查询别的同学呢?
接下来我们有四种解决方案:
- 全局变量
- 仿函数+构造方法
- 仿函数+绑定器
- 泛型模板函数+绑定器
1、全局变量实现:
我们要实现的就是想让搜索更具普遍性,那么将compare()函数里的局部变量变成可以随时修改的全局变量不就好了,只要在每次使用搜索之前更新一下全局变量的值就能实现这样的目的。
string id;//定义全局变量
bool compareId(stuInfo si){
return (si.id.compare(id)==0);
}
for(int i=0;i<m;i++){
id=idVec[i];//更新全局变量的值
std::vector<stuInfo>::iterator it = find_if(info.begin(),info.end(),compareId);
}
但是,这样不仅容易出错,而且最关键的是:不优雅!!!!
那么我们再来看看剩下的三种方法
2、仿函数+构造函数实现:
我们要实现的无非就是想在第三个参数函数里不只传递一个参数,还要传进另一个参数用以比较,那么仿函数就可以实现这一点,
编写一个新结构体/类,并编写一个仿函数(重载operator()函数),使得其与构造函数一起被调用,就能实现多传递一个参数进行比较的要求。
一般的,我们将需要比较的值作为结构体/类的变量,通过构造函数进行赋值。
然后,编写仿函数传递find_if()函数进行遍历的整个stuInfo对象
最后我们就可以在仿函数里拿到新结构体/类里的变量来和stuInfo对象里的变量进行比较,并返回bool值
仿函数一般长这样:
bool operator()(const ???& obj){
return ???????;
}
值得注意的是,这里的参数应该是常引用类型
具体实现如下(以结构体为例):
typedef struct ifIdMatch{
string m_id;
ifIdMatch(string id):m_id(id){}//构造函数
bool operator()(const stuInfo& si){//仿函数
return (si.id.compare(m_id)==0);
}
}ifIdMatch;
for(int i=0;i<m;i++){//遍历寻找符合idVec中第i个Id的学生信息
std::vector<stuInfo>::iterator it = find_if(info.begin(),info.end(),ifIdMatch(idVec[i]));
if(it!=info.end())//存在
Rank(*it);//进行后续操作,通过*it取得符合要求结构体信息
else cout<<"N/A";//不存在
}
3、仿函数+绑定器实现:
仿函数也可以放在绑定器里,从绑定器里获取另外一个参数而不是从结构体或者类中获取
这里需要用到一个一元函数结构体类型才能使用绑定器,否则会出错,因为需要指定argument type、result type等类型才能结合bind2nd使用,它的参数列表有三个参数,前两个是入参(argument type),第三个是返回值参数(result type)
当然,这里是一元函数的写法,下面即将要讲的绑定器+泛型模板函数实现里还有一种直接从普通函数变成符合要求的二元函数的方法
(这里只展示次参函数绑定器,其实还有首参、类绑定器):
struct compareId: binary_function<stuInfo, string, bool> {
bool operator()(const stuInfo& si, string id){
if (si.id.compare(id)==0) return true;
else return false;
}
};
for(int i=0;i<m;i++){//遍历寻找符合idVec中第i个Id的学生信息
std::vector<stuInfo>::iterator it = find_if(info.begin(),info.end(),bind2nd(compareId(),idVec[i]));
if(it!=info.end()) ????;//存在,进行后续操作,通过*it取得符合要求结构体信息
else ?????;//不存在
}
4、泛型模板函数+绑定器实现:
只要涉及到绑定器,就一定要写合乎规范的函数,但我们在这里定义的泛型模板函数并不符合要求,缺少argument type 和result type这两种参数类型的信息,但是,不要怕,很简单的,我们可以同伙ptr_fun()函数实现从普通函数到合规二元函数的转化
然后再用绑定器将二元函数与多余参数绑定变成一元函数即可
template<typename T> bool compareId(const T* s1 , const T* s2){
return s1->id.compare(s2->id);
}
for(int i=0;i<m;i++){//遍历寻找符合idVec中第i个Id的学生信息
stuInfo temp;
strcpy(temp.id,idVec[i]);
std::vector<stuInfo>::iterator it = find_if(info.begin(),info.end(),bind2nd(ptr_fun(compareId<stuInfo>), &temp));
if(it!=info.end()) ????;//存在,进行后续操作,通过*it取得符合要求结构体信息
else ?????;//不存在
}