baizy77 发表于 2018-10-25 18:27:22

【独家连载】Qt入门与提高:KS04_07 常用Qt类-QMap

本帖最后由 baizy77 于 2019-7-2 20:34 编辑

版权声明---------------------------------------------------------------------------------------------------------------------该文章原创于Qter开源社区(www.qter.org)作者: 女儿叫老白转载请注明出处!---------------------------------------------------------------------------------------------------------------------课程目录: 【独家连载】《Qt入门与提高-GUI产品开发》目录
网页版课程源码 提取码:1uy7
引言----------------------------------------------------------------------------------------------------------------------上一节我们讲了链表,本节我们给大家介绍Qt的映射处理类QMap。map的数据结构是键值对,也就是给定一个key,可以得到对应的value值,这对于我们访问数据提供了非常方便的手段,比如我们可以把学生的id作为key,把学生对象作为value,这样只要给出一个学员id,就可以方便、快速找到学员对象并访问其信息。正文----------------------------------------------------------------------------------------------------------------------    如果要使用QMap,需要包含其头文件。按我们之前讲过的规则,这个头文件同类名一致,所以,代码应该这样写:#include <Map>   同样的,我们也提供三种编程场景作为案例:    1.向QList添加成员并遍历、删除。    2.使用自定义类对象    3.需要注意的情况    下面我们分别看一下这三种场景。场景1. 向QList添加成员并遍历、删除。    此处,我们也使用了quint16作为成员对象的类型。/**
* @brief向QMap添加成员并遍历
* @return 无
*/
void example01(){

    //添加成员
    QMap<quint16,QString> mapObj;
    mapObj= "BeiJing";
    mapObj= "ShangHia";
    mapObj= "GuangZhou";
    mapObj= "ShenZhen";
    mapObj.insert(5, "XiaMen");
}
    我们演示了两种向QMap中添加成员的语法。    一种是直接用 map=value的方式,如:mapObj= "BeiJing";    另一种使用insert(key,value):    mapObj.insert(5,"XiaMen");    接下来,我们封装了printByIterator()接口对QMap进行遍历。void printByIterator(constQMap<quint16, QString>& mapObj)
{
    cout<< endl << "-------------- QMap ---------------"<< endl;
    cout<< "print members using iterator......" << endl;
    QMap<quint16,QString>::const_iterator iteMap = mapObj.begin();
    for(iteMap = mapObj.begin(); iteMap != mapObj.end(); iteMap++) {
      cout<< "-- key = " << iteMap.key() << ", value =" << iteMap.value().toLocal8Bit().data() << endl;
    }
}
    上述代码中我们使用了一个const迭代器iteMap,并将其初始化指向QMap的头部。 QMap<quint16,QString>::const_iterator iteMap = mapObj.begin();    用来判断迭代器是否已经遍历完毕的方法同QVector类似:      iteMap = mapObj.begin()    使用迭代器访问QMap的数据时,同QVector不太一样。因为QMap有key和value。访问key可以用: iteMap.key()    访问迭代器的value,可以用:iteMap.value()    用上述两个接口得到的对象跟QMap的key和value的类型一致,因此可以直接使用这种语法调用对象的接口,比如,如果value是一个QString,那么我们就可以这样编写代码: iteMap.value().toLocal8Bit().data()    QMap的查找功能通过find()接口实现,比如,我们如果查找QMap中是否存在5这个数据,可以这样写:iteMap = mapObj.find(5);
if (iteMap != mapObj.end()) {
    cout<< endl << "-------------- QMap ---------------"<< endl;
    cout<< "find member where key = 5, and value = " <<iteMap.value().toLocal8Bit().data() << endl;
} else {
    cout<< "cannot find member from map where key = 5." <<endl;
}    如上述代码所示,判断map中是否找到指定数据的方法也是将迭代器与map.end()进行比较:if (iteMap != mapObj.end())    QMap的删除操作使用erase()接口,参数是要删除的迭代器:// 删除
cout << endl <<"-------------- QMap ---------------" << endl;
if (iteMap != mapObj.end()) {
    cout<< "erase it from map." << endl;
    mapObj.erase(iteMap);
}场景2.使用自定义类对象    我们使用CMyClass类对象,并将其添加到QMap。方法同quint16类似。/**
* @brief使用自定义类对象
* @return 无
*/
void example02(){
    //添加成员
    QMap<CMyClass,uint> mapObj;
    CMyClassmyclass1(2011, "lisa");
    CMyClassmyclass2(2012, "mike");
    CMyClassmyclass3(2012, "mike");
    CMyClassmyclass4(2013, "john");
    CMyClassmyclass5(2013, "ping");
    CMyClassmyclass6(2025, "ping");
    //如果想让下面的语句编译通过并且按照预期执行,需要为CMyClass类提供拷贝构造函数,并重载operator<操作符。
   mapObj = 1;
   mapObj = 2;
   mapObj = 3;
   mapObj = 4;
   mapObj = 5;
      mapObj = 6;   
}      上面需要注意的是mapObj= 1,这句代码要求CMyClass类提供拷贝构造函数。其实如果我们不去编写CMyClass类的拷贝构造函数,程序也能编译通过,因为编译器会因为push_back()的调用为CMyClass类提供默认的拷贝构造函数。大家可以尝试封掉我们的拷贝构造函数做一下测试。另外,因为在实现查找功能时用到了QMap::find(),我们需要为CMyClass类重载operator<操作符。大家可以尝试封掉类CMyClass的operator<的重载定义和实现,看看会发生什么。bool CMyClass::operator<(constCMyClass& right) const {
   if (getId() < right.getId()) {
      return true;
   }
   else if ((getId() == right.getId()) && (getName() <right.getName())) {
      return true;
   }
   else {
      return false;
   }
}    重载自定义类的operator<()接口时需要注意保证逻辑的正确性。也就是说,要保证按照我们的算法运行时,传入a、b两个值时,我们的operator<()接口可以返回不同的结果。如果都返回true或者都返回false,那么我们的算法是错误的,将导致运行时异常。    在QMap中使用自定义类时,查找功能没有什么特别的:// 遍历成员
    cout<< endl << "-------------- QMap ---------------"<< endl;
   QMap<CMyClass, uint>::iterator iteMap;
   for (iteMap = mapObj.begin(); iteMap != mapObj.end(); iteMap++) {
      cout << "key =("<< iteMap.key().getId() << ", " <<iteMap.key().getName().toLocal8Bit().data() << "), value ="<< iteMap.value() << endl;
   }    从上述代码可以看出,iteMap.key().getName()这种编码就是对key对象的调用CMyClass::getName()接口,因为key()的类型就是CMyClass.    自定义类对象的查找功能,也需要先定义一个对象,然后调用QMap的find(): // 查找&删除
    cout<< endl << "-------------- QMap ---------------"<< endl;
    cout<< "begin find member in QMap......" << endl;
    CMyClassmyclassx(2013, "john");
    iteMap= mapObj.find(myclassx);
    if(iteMap != mapObj.end()) {
      ……
    }
    else{
      cout<< "cannot find myclassx in map" << endl;
    }
场景3.需要注意的情况    如果我们的value是一个QStringList之类的list或者vector等容器,那么有些编译器不允许对未经初始化的value直接采取插入等修改操作,比如:// 有的编译器执行如下代码时报错
   std::map<uint, QStringList> mapObj1;
   std::map<uint, QStringList>::iterator iteMap1;
   iteMap1 = mapObj1.find(1);
   if (iteMap1 == mapObj1.end())   {
      cout << "not found!"<< endl;
         mapObj1.push_back("hello");// error
}
    上文中,mapObj1尚未初始化,如果直接push_back(),将导致错误。    应该改为:// 有的编译器不允许用本行的代码直接对未初始化的value操作,
// 而应该用下面的3行代码:先定义一个QStringList对象lst,把数据插入lst,然后把lst作为value插入到QMap

QStringList lst;
lst.push_back("hello");
mapObj1 = lst;
    也就是需要用一个临时变量lst处理一下。结语----------------------------------------------------------------------------------------------------------------------   本节我们介绍了映射类QMap的用法,它跟stl的map有很多相似的地方。我们一般在界面类程序中选择QMap,而且服务类程序中选择stl的map或者其他第三方库的map。当数据量很大时,自己建立数组或链表进行查找性能是很差的,还是要用QMap来处理以提高代码性能。
上一节:KS04-06常用Qt类-QList下一节:KS04-08   编写自己的公共类库
页: [1]
查看完整版本: 【独家连载】Qt入门与提高:KS04_07 常用Qt类-QMap