找回密码
 立即注册
收起左侧

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

0
回复
6720
查看
[复制链接]
累计签到:41 天
连续签到:1 天
来源: 原创 2018-10-25 18:27:22 显示全部楼层 |阅读模式

马上注册,查看详细内容!注册请先查看:注册须知

您需要 登录 才可以下载或查看,没有帐号?立即注册

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

版权声明
---------------------------------------------------------------------------------------------------------------------
该文章原创于Qter开源社区(www.qter.org
作者: 女儿叫老白
转载请注明出处!
---------------------------------------------------------------------------------------------------------------------
----------------------------------------------------------------------------------------------------------------------
上一节我们讲了链表,本节我们给大家介绍Qt的映射处理类QMap。map的数据结构是键值对,也就是给定一个key,可以得到对应的value值,这对于我们访问数据提供了非常方便的手段,比如我们可以把学生的id作为key,把学生对象作为value,这样只要给出一个学员id,就可以方便、快速找到学员对象并访问其信息。
正文
----------------------------------------------------------------------------------------------------------------------
    如果要使用QMap,需要包含其头文件。按我们之前讲过的规则,这个头文件同类名一致,所以,代码应该这样写:
  1. #include <Map>
复制代码
   同样的,我们也提供三种编程场景作为案例:
    1.  向QList添加成员并遍历、删除。
    2.  使用自定义类对象
    3.  需要注意的情况
    下面我们分别看一下这三种场景。
场景1. 向QList添加成员并遍历、删除。
    此处,我们也使用了quint16作为成员对象的类型。
  1. /**  
  2. * @brief  向QMap添加成员并遍历  
  3. * @return 无  
  4. */  
  5. void example01(){
  6.   
  7.     //  添加成员  
  8.     QMap<quint16,  QString> mapObj;  
  9.     mapObj[1]  = "BeiJing";  
  10.     mapObj[2]  = "ShangHia";  
  11.     mapObj[3]  = "GuangZhou";  
  12.     mapObj[4]  = "ShenZhen";
  13.     mapObj.insert(5, "XiaMen");  
  14. }
复制代码

    我们演示了两种向QMap中添加成员的语法。
    一种是直接用 map[key]=value的方式,如:
mapObj[1]= "BeiJing";
    另一种使用insert(key,value):
    mapObj.insert(5,"XiaMen");
    接下来,我们封装了printByIterator()接口对QMap进行遍历。
  1.   void printByIterator(const  QMap<quint16, QString>& mapObj)  
  2. {  
  3.     cout  << endl << "-------------- QMap ---------------"  << endl;  
  4.     cout  << "print members using iterator......" << endl;  
  5.     QMap<quint16,  QString>::const_iterator iteMap = mapObj.begin();  
  6.     for  (iteMap = mapObj.begin(); iteMap != mapObj.end(); iteMap++) {  
  7.         cout  << "-- key = " << iteMap.key() << ", value =  " << iteMap.value().toLocal8Bit().data() << endl;  
  8.     }  
  9. }
复制代码

    上述代码中我们使用了一个const迭代器iteMap,并将其初始化指向QMap的头部。
  1. QMap<quint16,  QString>::const_iterator iteMap = mapObj.begin();  
复制代码
    用来判断迭代器是否已经遍历完毕的方法同QVector类似:
  1.       iteMap = mapObj.begin()  
复制代码
    使用迭代器访问QMap的数据时,同QVector不太一样。因为QMap有key和value。访问key可以用:
  1. iteMap.key()  
复制代码
    访问迭代器的value,可以用:
  1.   iteMap.value()  
复制代码
    用上述两个接口得到的对象跟QMap的key和value的类型一致,因此可以直接使用这种语法调用对象的接口,比如,如果value是一个QString,那么我们就可以这样编写代码:
  1. iteMap.value().toLocal8Bit().data()  
复制代码
    QMap的查找功能通过find()接口实现,比如,我们如果查找QMap中是否存在5这个数据,可以这样写:
  1.   iteMap = mapObj.find(5);  
  2. if (iteMap != mapObj.end()) {  
  3.     cout  << endl << "-------------- QMap ---------------"  << endl;  
  4.     cout  << "find member where key = 5, and value = " <<  iteMap.value().toLocal8Bit().data() << endl;  
  5. } else {  
  6.     cout  << "cannot find member from map where key = 5." <<  endl;  
  7. }  
复制代码
    如上述代码所示,判断map中是否找到指定数据的方法也是将迭代器与map.end()进行比较:
  1.   if (iteMap != mapObj.end())
复制代码
    QMap的删除操作使用erase()接口,参数是要删除的迭代器:
  1. // 删除  
  2. cout << endl <<  "-------------- QMap ---------------" << endl;  
  3. if (iteMap != mapObj.end()) {  
  4.     cout  << "erase it from map." << endl;  
  5.     mapObj.erase(iteMap);  
  6. }
复制代码
场景2.  使用自定义类对象
    我们使用CMyClass类对象,并将其添加到QMap。方法同quint16类似。
  1. /**  
  2. * @brief  使用自定义类对象  
  3. * @return 无  
  4. */  
  5. void example02(){  
  6.     //  添加成员  
  7.     QMap<CMyClass,  uint> mapObj;  
  8.     CMyClass  myclass1(2011, "lisa");  
  9.     CMyClass  myclass2(2012, "mike");  
  10.     CMyClass  myclass3(2012, "mike");  
  11.     CMyClass  myclass4(2013, "john");  
  12.     CMyClass  myclass5(2013, "ping");  
  13.     CMyClass  myclass6(2025, "ping");  
  14.     //  如果想让下面的语句编译通过并且按照预期执行,需要为CMyClass类提供拷贝构造函数,并重载operator<操作符。  
  15.      mapObj[myclass1] = 1;  
  16.      mapObj[myclass2] = 2;  
  17.      mapObj[myclass3] = 3;  
  18.      mapObj[myclass4] = 4;  
  19.      mapObj[myclass5] = 5;
  20.         mapObj[myclass6] = 6;   
  21. }     
复制代码
   上面需要注意的是mapObj[myclass1]= 1,这句代码要求CMyClass类提供拷贝构造函数。其实如果我们不去编写CMyClass类的拷贝构造函数,程序也能编译通过,因为编译器会因为push_back()的调用为CMyClass类提供默认的拷贝构造函数。大家可以尝试封掉我们的拷贝构造函数做一下测试。
另外,因为在实现查找功能时用到了QMap::find(),我们需要为CMyClass类重载operator<操作符。大家可以尝试封掉类CMyClass的operator<的重载定义和实现,看看会发生什么。
  1. bool CMyClass::operator<(const  CMyClass& right) const {  
  2.      if (getId() < right.getId()) {  
  3.         return true;  
  4.      }  
  5.      else if ((getId() == right.getId()) && (getName() <  right.getName())) {  
  6.         return true;  
  7.      }  
  8.      else {  
  9.         return false;  
  10.      }  
  11. }
复制代码
    重载自定义类的operator<()接口时需要注意保证逻辑的正确性。也就是说,要保证按照我们的算法运行时,传入a、b两个值时,我们的operator<()接口可以返回不同的结果。如果都返回true或者都返回false,那么我们的算法是错误的,将导致运行时异常。
    在QMap中使用自定义类时,查找功能没有什么特别的:
  1. // 遍历成员  
  2.     cout  << endl << "-------------- QMap ---------------"  << endl;  
  3.      QMap<CMyClass, uint>::iterator iteMap;  
  4.      for (iteMap = mapObj.begin(); iteMap != mapObj.end(); iteMap++) {  
  5.         cout << "key =  ("<< iteMap.key().getId() << ", " <<  iteMap.key().getName().toLocal8Bit().data() << "), value =  "<< iteMap.value() << endl;  
  6.    }
复制代码
    从上述代码可以看出,iteMap.key().getName()这种编码就是对key对象的调用CMyClass::getName()接口,因为key()的类型就是CMyClass.
    自定义类对象的查找功能,也需要先定义一个对象,然后调用QMap的find():
  1. // 查找&删除  
  2.     cout  << endl << "-------------- QMap ---------------"  << endl;  
  3.     cout  << "begin find member in QMap......" << endl;  
  4.     CMyClass  myclassx(2013, "john");  
  5.     iteMap  = mapObj.find(myclassx);  
  6.     if  (iteMap != mapObj.end()) {  
  7.         ……  
  8.     }  
  9.     else  {  
  10.         cout  << "cannot find myclassx in map" << endl;  
  11.     }  
复制代码

场景3.  需要注意的情况
    如果我们的value是一个QStringList之类的list或者vector等容器,那么有些编译器不允许对未经初始化的value直接采取插入等修改操作,比如:
  1. // 有的编译器执行如下代码时报错  
  2.      std::map<uint, QStringList> mapObj1;  
  3.      std::map<uint, QStringList>::iterator iteMap1;  
  4.      iteMap1 = mapObj1.find(1);  
  5.      if (iteMap1 == mapObj1.end())     {  
  6.         cout << "not found!"  << endl;  
  7.          mapObj1[1].push_back("hello");  // error  
  8. }  
复制代码

    上文中,mapObj1[1]尚未初始化,如果直接push_back(),将导致错误。
    应该改为:
  1. // 有的编译器不允许用本行的代码直接对未初始化的value操作,
  2. // 而应该用下面的3行代码:先定义一个QStringList对象lst,把数据插入lst,然后把lst作为value插入到QMap

  3. QStringList lst;
  4. lst.push_back("hello");
  5. mapObj1[1] = lst;
复制代码

    也就是需要用一个临时变量lst处理一下。
结语
----------------------------------------------------------------------------------------------------------------------
   本节我们介绍了映射类QMap的用法,它跟stl的map有很多相似的地方。我们一般在界面类程序中选择QMap,而且服务类程序中选择stl的map或者其他第三方库的map。当数据量很大时,自己建立数组或链表进行查找性能是很差的,还是要用QMap来处理以提高代码性能。

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

公告
可以关注我们的微信公众号yafeilinux_friends获取最新动态,或者加入QQ会员群进行交流:190741849、186601429(已满) 我知道了