baizy77的个人主页

http://www.qtcn.org/bbs/u/88608  [收藏] [复制]

baizy77

小白

  • 2

    关注

  • 8

    粉丝

  • 1

    访客

  • 等级:新手上路
  • 总积分:0
  • 男,1977-11-27

最后登录:2021-01-06

更多资料

日志

4.6 案例10 使用QList处理数据集

2020-12-24 14:56
本案例对应的源代码目录:src/chapter04/ks04_06。
本节介绍Qt的数据处理类QList,QList实际内部还是一个指针数组。如果要使用QList,需要包含其头文件<QList>。本案例也设计了三种编程场景对QList的使用进行介绍。
(1)向QList中添加成员并遍历。
(2)向QList中添加自定义类的对象。
(3)向QList中添加自定义类对象的指针。
下面进行详细介绍。
1.向QList中添加成员并遍历
代码清单4-44中介绍了向QList中添加成员并遍历的方法,这里也使用quint16作为成员对象的类型。
代码清单4-44

void example01(){                        
    // 添加成员
    QList<quint16> lstObj;
    lstObj.push_back(2011);
    lstObj.push_back(2033);
    lstObj.push_back(2033);
    lstObj.push_back(2042);
    lstObj.push_back(2045);
    // push_front
    lstObj.push_front(2046);
    ...
}

向QList中添加成员也用到了push_back()。接下来使用几种不同的方法对链表进行遍历。为了方便,把遍历、打印链表的代码封装为几个接口,以便在其他案例中使用,如代码清单4-45所示。在标号①处,因为传入的参数类型是const引用,所以在printByIterator()接口实现代码中的标号②处,需要使用QList<quint16>::const_iterator定义常量迭代器对象iteList来访问链表。为iteList赋值时需要调用lstObj对象的constBegin()接口,在判断链表首尾时使用QList的constBegin()、constEnd()接口。同理,在标号④处的倒序访问接口中,用QList<quint16>::const_reverse_iterator定义迭代器对象,并且在标号⑤处用的是QList的crbegin()、crend()接口获取链表的倒序首尾。
代码清单4-45

void example01(){
    ...
    // 遍历成员-使用下标
    printByIndex(lstObj);
    // 遍历成员-使用迭代器(正序)
    printByIterator(lstObj);
    // 遍历成员-使用迭代器(倒序)
    printByIteratorReverse(lstObj);
    ...
}
// 遍历成员-使用正序迭代器
void printByIterator(const QList<quint16>& lstObj) {                            
    cout << endl << "-------------- QList ---------------" << endl;
    cout << "print members using iterator......" << endl;
    QList<quint16>::const_iterator iteList = lstObj.constBegin();              
    for (iteList = lstObj.constBegin(); iteList != lstObj.constEnd(); iteList++) {
        cout << "    " << *iteList << endl;
    }
}
// 遍历成员-使用倒序迭代器
void printByIteratorReverse(const QList<quint16>& lstObj){
    cout << endl << "-------------- QList ---------------" << endl;
    cout << "print members using iterator reverse......" << endl;
    QList<quint16>::const_reverse_iterator iteList;                              
   for (iteList = lstObj.crbegin(); iteList != lstObj.crend();iteList++){                                                                      
        cout << "    " << *iteList << endl;
    }
}
// 遍历成员-使用下标
void printByIndex(const QList<quint16>& lstObj){
    cout << endl << "-------------- QList ---------------" << endl;
    cout << "print members using index......" << endl;
    int idxList = 0;
    for (idxList = 0; idxList < lstObj.size(); idxList++) {
        cout << "    lstObj[" << idxList << "] =" << lstObj[idxList] << endl;
    }
}

2.向QList中添加自定义类的对象
向QList中添加自定义类的对象时使用CMyClass类,方法同QVector用法类似,见代码清单4-46。
代码清单4-46

void example02(){
    // 添加成员
    QList<CMyClass> lstObj;
    CMyClass myclass1(2011, "lisa");
    CMyClass myclass2(2012, "mike");
    CMyClass myclass3(2012, "mike");
    CMyClass myclass4(2013, "john");
    CMyClass myclass5(2013, "ping");
    CMyClass myclass6(2025, "ping");
    // 如果想让下面的语句编译通过并且按照预期执行,需要为CMyClass类提供拷贝构造函数
    lstObj.push_back(myclass1);                                                    
    lstObj.push_back(myclass2);
    lstObj.push_back(myclass3);
    lstObj.push_back(myclass4);
    lstObj.push_back(myclass5);
    lstObj.push_back(myclass6);
    // 遍历成员
    cout << endl << "-------------- QList ---------------" << endl;
    cout << "print members using idx......" << endl;
    int idxList = 0;
    for (idxList = 0; idxList < lstObj.size(); idxList++) {
        cout << "    lstObj[" << idxList << "] : id = "
             << lstObj[idxList].getId() << ", name = "
             << lstObj[idxList].getName().toLocal8Bit().data() << endl;
    }
     // 查找
    cout << endl << "-------------- QList ---------------" << endl;
    cout << "begin find member in QList......" << endl;
    CMyClass myclassx(2013, "john");
    QList<CMyClass>::iterator iteList = std::find(lstObj.begin(), lstObj.end(), myclassx);
    if (iteList != lstObj.end()) {
        cout << "find myclassx in list." << endl;
    }
    else {
        cout << "cannot find myclassx in list" << endl;
    }
}

代码清单4-46中需要注意的是标号①处的lstObj.push_back(myclass1),这句代码要求CMyClass类提供拷贝构造函数。其实如果不写CMyClass类的拷贝构造函数,程序也能构建成功,因为编译器会为CMyClass类提供默认的拷贝构造函数。尝试一下封掉CMyClass的拷贝构造函数,看看是否能将项目构建成功。其实,封掉CMyClass的拷贝构造函数后,push_back()可以成功调用,但是程序却会出现运行时异常。这是为什么呢?因为编译器提供的默认拷贝构造函数仅仅执行按位复制,也就是将对象的成员变量的值一对一复制,而CMyClass类的成员中有指针,如果按位复制那么就不会为指针变量重新申请内存而是将它和被复制对象指向同一块内存。在CMyClass析构时会出现将同一块内存多次delete的问题,导致出现异常。所以,在本案例中应该为类编写显式的拷贝构造函数。
另外,因为在实现查找功能时用到了std::find(),所以仍然需要为CMyClass类重载operator==操作符。尝试封掉类CMyClass的operator==的重载操作符的定义和实现代码,看看会有什么结果。哈哈,编译器会报错。

error C2678: 二进制“==”: 没有找到接受“CMyClass”类型的左操作数的运算符(或没有可接受的转换)。

编译器明确提示需要为CMyClass提供operator==的重载,所以如果要使用std::find(),那么对类的operator==操作符的重载是不可缺少的。
3.向QList中添加自定义类对象的指针
向QList中添加自定义类对象的指针与QVector的案例类似,见代码清单4-47。
代码清单4-47

void example03() {
    // 添加成员
    QList<CMyClass*> lstObj;
    CMyClass* pMyclass1 = new CMyClass(2011, "lisa");
    CMyClass* pMyclass2 = new CMyClass(2012, "mike");
    CMyClass* pMyclass3 = new CMyClass(2012, "mike");
    CMyClass* pMyclass4 = new CMyClass(2013, "john");
    CMyClass* pMyclass5 = new CMyClass(2013, "ping");
    CMyClass* pMyclass6 = new CMyClass(2025, "ping");
    // 无须为CMyClass类提供拷贝构造函数
    lstObj.push_back(pMyclass1);
    lstObj.push_back(pMyclass2);
    lstObj.push_back(pMyclass3);
    lstObj.push_back(pMyclass4);
    lstObj.push_back(pMyclass5);
    lstObj.push_back(pMyclass6);
    // 遍历成员
    cout << endl << "-------------- QList ---------------" << endl;
    cout << "print members in custom defined class using idx......" << endl;
    int idxList = 0;
    for (idxList = 0; idxList < lstObj.size(); idxList++) {
        cout << "    lstObj[" << idxList << "] : id = "
             << lstObj[idxList]->getId() << ", name = "
             << lstObj[idxList]->getName().toLocal8Bit().data() << endl;
    }
    // 退出前要释放内存
    // 方法1,使用下标遍历
    cout << endl << "-------------- QList ---------------" << endl;
    cout << "desctruct members before exit......" << endl;
    idxList = 0;
    for (idxList = 0; idxList < lstObj.size(); idxList++) {
        cout << "    deleting " << idxList << ", id = "
             << lstObj[idxList]->getId() << ", name = "
             << lstObj[idxList]->getName().toLocal8Bit().data() << endl;
        delete lstObj[idxList];
    }
    // 方法2,使用迭代器遍历                                                  ①
    //QList<CMyClass*>::iterator iteList = lstObj.begin();
    //for (iteList = lstObj.begin(); iteList != lstObj.end(); iteList++, idxList++) {
    //    if (NULL != *iteList) {
    //        delete *iteList;
    //    }
    //}
    lstObj.clear();
}

代码清单4-47中的标号①处,方法2的代码被封掉的原因也是因为防止重复析构。此处给出方法2是为了演示用迭代器的方式遍历列表的成员。
本节介绍了链表类QList的用法,它跟QVector有很多相似的地方。不同之处在于,如果对性能要求更高一些,应考虑QVector。QLinkedList才是Qt真正意义上的链表,QLinkedList实际上跟std::list是一样的,也是双向链表。
----------------------------------------------------------------------------------------------------------------------------------------------
《Qt 5/PyQt 5实战指南》目录

分类:Qr入门与提高|回复:2|浏览:419|全站可见|转载
 

下一篇:

上一篇: 4.5 案例9 使用QVector处理数组

 
删除

lixinwei:QList根本就不是链表,QLinkedList才是

2020-12-29 20:15 -

baizy77:您说的对,已更正。

2021-01-05 13:16

Powered by phpwind v8.7 Certificate Copyright Time now is:05-02 13:53
©2005-2016 QTCN开发网 版权所有 Gzip disabled