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

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

0
回复
7988
查看
[复制链接]
累计签到:41 天
连续签到:1 天
来源: 原创 2018-10-19 15:48:28 显示全部楼层 |阅读模式

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

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

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

版权声明
---------------------------------------------------------------------------------------------------------------------
该文章原创于Qter开源社区(www.qter.org
作者: 女儿叫老白
转载请注明出处!
---------------------------------------------------------------------------------------------------------------------
引言
----------------------------------------------------------------------------------------------------------------------
上一节我们讲了数组,本节我们给大家介绍Qt的链表处理类QList。链表数据结构的特点是内存并不连续,但是增加、删除链表节点非常方便、高效,因为只需要在增加或删除节点的位置进行修改而不会影响其他节点。
正文
----------------------------------------------------------------------------------------------------------------------
    如果要使用QList,需要包含其头文件。按我们之前讲过的规则,这个头文件同类名一致,所以,代码应该这样写
  1. #include <QList>  
复制代码
    同样的,我们也提供三种编程场景作为案例:
    1.  向QList添加成员并遍历。
    2.  使用自定义类对象
    3.  使用自定义类对象指针
    下面我们分别看一下这三种场景。
场景1. 向QList添加成员并遍历。
    此处,我们也使用了quint16作为成员对象的类型。
  1. /**
  2. * @brief  向QList添加成员并遍历。  
  3. * @return 无  
  4. */  
  5. void example01(){     
  6.     //  添加成员  
  7.     QList<quint16>  lstObj;  
  8.     lstObj.push_back(2011);  
  9.     lstObj.push_back(2033);  
  10.     lstObj.push_back(2033);  
  11.     lstObj.push_back(2042);  
  12.     lstObj.push_back(2045);  
  13.     //  push_front  
  14.     lstObj.push_front(2046);
复制代码
    向链表中添加成员也用到了push_back()。
接下来,我们使用不同的方法对链表进行遍历。为了方便,我们把遍历并打印链表的代码封装为几个接口,因为在其他案例中也要用到。
  1. //  遍历成员-使用下标
  2.     printByIndex(lstObj);  
  3.     //  遍历成员-使用迭代器(正序)  
  4.     printByIterator(lstObj);  
  5.     //  遍历成员-使用迭代器(倒序)  
  6.     printByIteratorReverse(lstObj);  
复制代码
    下面是这几个接口的实现,同QVector的用法类似,因此不再赘述。
  1. // 遍历成员-使用迭代器(正序)  
  2. void printByIterator(const  QList<quint16>& lstObj) {  
  3.     cout  << endl << "-------------- QList ---------------"  << endl;  
  4.     cout  << "print members using iterator......" << endl;  
  5.     QList<quint16>::const_iterator  iteList = lstObj.begin();  
  6.     for  (iteList = lstObj.begin(); iteList != lstObj.end(); iteList++) {  
  7.         cout  << "    " <<  *iteList << endl;  
  8.     }
  9. }  
  10. // 遍历成员-使用迭代器(倒序)  
  11. void printByIteratorReverse(const  QList<quint16>& lstObj){  
  12.     cout  << endl << "-------------- QList ---------------"  << endl;  
  13.     cout  << "print members using iterator reverse......" <<  endl;  
  14.     QList<quint16>::const_reverse_iterator  iteList;  
  15.     for  (iteList = lstObj.rbegin(); iteList != lstObj.rend(); iteList++) {  
  16.         cout  << "    " <<  *iteList << endl;  
  17.     }  
  18. }
  19.   
  20. // 遍历成员-使用下标  
  21. void printByIndex(const  QList<quint16>& lstObj){  
  22.     cout  << endl << "-------------- QList ---------------"  << endl;  
  23.     cout  << "print members using idx......" << endl;  
  24.     int  idxList = 0;  
  25.     for  (idxList = 0; idxList < lstObj.size(); idxList++) {  
  26.         cout  << "    lstObj["  << idxList << "] =" << lstObj[idxList] <<  endl;  
  27.     }  
  28. }
复制代码
    定义迭代器时,使用的语法是: 链表对象的类型::iterator。即上述代码中的:
  1.        QList<quint16>::iterator
复制代码
    用来判断迭代器是否已经遍历完毕的方法同QVector类似:
  1. iteList  != lstObj.end()  
复制代码
场景2.  使用自定义类对象
    我们使用CMyClass类对象,并将其添加到链表。方法同QVector类似。
  1. /**
  2. * @brief  使用自定义类对象  
  3. * @return 无  
  4. */  
  5. void example02(){  
  6.     //  添加成员  
  7.     QList<CMyClass>  lstObj;  
  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类提供拷贝构造函数  
  15.     lstObj.push_back(myclass1);  
  16.     lstObj.push_back(myclass2);  
  17.     lstObj.push_back(myclass3);  
  18.     lstObj.push_back(myclass4);  
  19.     lstObj.push_back(myclass5);  
  20.     lstObj.push_back(myclass6);  
  21.   
  22.     //  遍历成员  
  23.     cout  << endl << "-------------- QList ---------------"  << endl;  
  24.     cout  << "print members using idx......" << endl;  
  25.     int  idxList = 0;  
  26.     for  (idxList = 0; idxList < lstObj.size(); idxList++) {  
  27.         cout  << "    lstObj["  << idxList << "] : id = " << lstObj[idxList].getId()  << ", name = " <<  lstObj[idxList].getName().toLocal8Bit().data() << endl;  
  28.     }
  29.    
  30.     //  查找  
  31.     cout  << endl << "-------------- QList ---------------"  << endl;  
  32.     cout  << "begin find member in QList......" << endl;  
  33.     CMyClass  myclassx(2013, "john");  
  34.     QList<CMyClass>::iterator  iteList = std::find(lstObj.begin(), lstObj.end(), myclassx);  
  35.     if  (iteList != lstObj.end()) {  
  36.         cout  << "find myclassx in list." << endl;  
  37.     }  
  38.     else  {  
  39.         cout  << "cannot find myclassx in list" << endl;  
  40.     }
  41. }
复制代码
    上面需要注意的是lstObj.push_back(myclass1)。这句代码要求CMyClass类提供拷贝构造函数。其实如果我们不去编写CMyClass类的拷贝构造函数,程序也能编译通过,因为编译器会因为push_back()的调用为CMyClass类提供默认的拷贝构造函数。大家可以尝试封掉我们的拷贝构造函数做一下测试。
封掉CMyClass的拷贝构造函数后,push_back()可以成功调用,但是程序却会运行异常。为什么呢?因为编译器提供的拷贝构造函数仅仅执行位拷贝,也就是会将对象的成员变量的值一对一拷贝赋值,而CMyClas类的成员中有指针,如果按位拷贝那么就不会重新申请内存而是指向同一块内存,结果在CMyClass析构时会出现将同一块内存多次delete的问题,因此引发异常。
所以还是养成习惯,应该自己动手为类提供拷贝构造函数。
另外,因为在实现查找功能时用到了std::find()算法,所以仍然需要为CMyClass类重载operator==操作符。大家可以尝试封掉类CMyClass的operator==的重载定义和实现,看看会有什么结果。哈哈,编译器告诉我们;
    errorC2678: 二进制“==”: 没有找到接受“CMyClass”类型的左操作数的运算符(或没有可接受的转换)
编译器主动告诉我们需要为CMyClass提供==的定义。所以重载的代码是不可少的。
场景3.  使用自定义类对象指针
    使用指针的情形与QVector类似。
  1. /**  
  2. * @brief  使用自定义类指针  
  3. * @return 无  
  4. */  
  5. void example03() {  
  6.     //  添加成员  
  7.     QList<CMyClass*>  lstObj;  
  8.     CMyClass*  pMyclass1 = new CMyClass(2011, "lisa");  
  9.     CMyClass*  pMyclass2 = new CMyClass(2012, "mike");  
  10.     CMyClass*  pMyclass3 = new CMyClass(2012, "mike");  
  11.     CMyClass*  pMyclass4 = new CMyClass(2013, "john");  
  12.     CMyClass*  pMyclass5 = new CMyClass(2013, "ping");  
  13.     CMyClass*  pMyclass6 = new CMyClass(2025, "ping");  
  14.       //  无需为CMyClass类提供拷贝构造函数  
  15.     lstObj.push_back(pMyclass1);  
  16.     lstObj.push_back(pMyclass2);  
  17.     lstObj.push_back(pMyclass3);  
  18.     lstObj.push_back(pMyclass4);  
  19.     lstObj.push_back(pMyclass5);  
  20.     lstObj.push_back(pMyclass6);   
  21.     //  遍历成员  
  22.     cout  << endl << "-------------- QList ---------------"  << endl;  
  23.     cout  << "print members in custom defined class using idx......"  << endl;  
  24.     int  idxList = 0;  
  25.     for  (idxList = 0; idxList < lstObj.size(); idxList++) {  
  26.         cout  << "    lstObj["  << idxList << "] : id = " <<  lstObj[idxList]->getId() << ", name = " <<  lstObj[idxList]->getName().toLocal8Bit().data() << endl;  
  27.     }  
  28.     //  退出前要释放内存  
  29.     //  方法1,使用下标遍历  
  30.     cout  << endl << "-------------- QList ---------------"  << endl;  
  31.     cout  << "desctruct members before exit......" << endl;  
  32.     idxList  = 0;  
  33.     for  (idxList = 0; idxList < lstObj.size(); idxList++) {  
  34.         cout  << "    deleting "  << idxList << ", id = " <<  lstObj[idxList]->getId() << ", name = " <<  lstObj[idxList]->getName().toLocal8Bit().data() << endl;
  35.               delete  lstObj[idxList];  
  36.     }
  37.   
  38.     //  方法2,使用迭代器遍历  
  39.     //QList<CMyClass*>::iterator  iteList = lstObj.begin();  
  40.     //for  (iteList = lstObj.begin(); iteList != lstObj.end(); iteList++, idxList++) {  
  41.     //  if (NULL != *iteList) {  
  42.     //      delete *iteList;  
  43.     //  }  
  44.     //}  
  45.     lstObj.clear();  
  46. }
复制代码

结语
----------------------------------------------------------------------------------------------------------------------
   本节我们介绍了链表类QList的用法,它跟QVector有很多相似的地方。唯一不同的地方是数据结构本身所代表的含义。当我们希望通过下标快速访问连续内存对象时,可以使用数组(QVector);当我们希望频繁高效的增删节点时,就要用到链表。这两个类各有各自的特点和应用场景,互相不可替代。下一章节我们将为大家介绍映射,让我们共同期待。


回复

使用道具 举报

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

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