• Qt中容器类应该如何存储对象


    Qt提供了丰富的容器类型,如:QList、QVector、QMap等等。详细的使用方法可以参考官方文档,网上也有很多示例文章,不过大部分文章的举例都是使用基础类型:如int、QString等。如果我们要存储一个对象类型,应该如何做呢?—— 当然是和int类型一样操作,因为这些容器类都是泛型的。不过,我们今天要讨论的不是容器类的使用用法,而是容器存储的对象内存如何释放的问题。

    (这里提到了对象类型是指 Class/Struct,可以继承自QObject,也可以是普通的C++类。)

    下面以QList<T>为例,直接通过代码来看一下以下几种情况的内存释放。

    0.测试前的一点点准备

     1 // testobj.h
     2 #ifndef TESTOBJ_H
     3 #define TESTOBJ_H
     4 
     5 #include <QObject>
     6 
     7 // 测试对象(也可以不继承QObject)
     8 class TestObj : public QObject
     9 {
    10     Q_OBJECT
    11 public:
    12     explicit TestObj(QObject *parent = 0);
    13 
    14     ~TestObj();
    15 
    16     TestObj(const TestObj& obj);
    17 
    18     TestObj& operator=(const TestObj& obj);
    19 
    20 };
    21 
    22 #endif // TESTOBJ_H

    实现TestObj

     1 // testobj.cpp
     2 #include "testobj.h"
     3 #include <QDebug>
     4 
     5 // 构造时输出log
     6 TestObj::TestObj(QObject *parent) : QObject(parent)
     7 {
     8     qDebug()<<"TestObj C.tor.";
     9 }
    10 
    11 // 析构时输出log
    12 TestObj::~TestObj(){
    13     qDebug()<<"TestObj D.tor.";
    14 }
    15 
    16 // 拷贝时输出log
    17 TestObj::TestObj(const TestObj& obj){
    18 
    19     qDebug()<<"TestObj COPY.";
    20 }
    21 
    22 // 赋值时输出log
    23 TestObj& TestObj::operator=(const TestObj& obj){
    24 
    25     qDebug()<<"TestObj =.";
    26     return *this;
    27 }

    1.在栈上创建对象,然后添加容器

     1 // main.cpp
     2 #include <QCoreApplication>
     3 #include <QList>
     4 
     5 #include "testobj.h"
     6 
     7 int main(int argc, char *argv[])
     8 {
     9     QCoreApplication a(argc, argv);
    10     
    11     {
    12         // Test one
    13         {
    14             TestObj obj;
    15             {
    16                 QList<TestObj> objList;
    17                 objList.append(obj);
    18             }
    19 
    20             qDebug()<<"ONE: "<<"objList release.";
    21         }
    22 
    23         qDebug()<<"ONE: "<<"TestObj release.";
    24         qDebug()<<endl;
    25     }
    26     
    27     return a.exec();
    28 }

    运行结果:

    结论:

    对象加入到容器时会发生拷贝,容器析构时,容器内的对象也会析构。

    2. 在堆上创建对象,然后添加到容器 

     1 // main.cpp
     2 #include <QCoreApplication>
     3 #include <QList>
     4 
     5 #include "testobj.h"
     6 
     7 int main(int argc, char *argv[])
     8 {
     9     QCoreApplication a(argc, argv);
    10     
    11     {
    12         // test tow
    13         {
    14             TestObj *obj = new TestObj;
    15             {
    16                 QList<TestObj*> objList;
    17                 objList.append(obj);
    18             }
    19             qDebug()<<"TWO: "<<"objList release.";
    20         }
    21 
    22         qDebug()<<"TWO: "<<"TestObj release? NO!";
    23         qDebug()<<endl;
    24     }
    25     
    26     return a.exec();
    27 }

    运行结果:

    结论:

    对象不会发生拷贝,但容器析构后容器内的对象并未析构

    3. 使用Qt智能指针来管理堆上的对象,然后添加到容器

     1 // main.cpp
     2 #include <QCoreApplication>
     3 #include <QList>
     4 #include <QSharedPointer>
     5 
     6 #include "testobj.h"
     7 
     8 int main(int argc, char *argv[])
     9 {
    10     QCoreApplication a(argc, argv);
    11     
    12     {
    13         // test three
    14         {
    15             QSharedPointer<TestObj> obj(new TestObj);
    16             {
    17                 QList<QSharedPointer<TestObj>> objList;
    18                 objList.append(obj);
    19             }
    20             qDebug()<<"THREE: "<<"objList release.";
    21         }
    22 
    23         qDebug()<<"THREE: "<<"TestObj release? YES!";
    24         qDebug()<<endl;
    25     }
    26     
    27     return a.exec();
    28 }

    运行结果:

    结论:

     对象不会发生拷贝,容器析构的时候,容器内对象并未析构,但超过作用域后,智能指针管理的对象会析构。

    4.给测试对象一个parent(父对象),然后进行上述测试

     1 // main.cpp
     2 #include <QCoreApplication>
     3 #include <QList>
     4 
     5 #include "testobj.h"
     6 
     7 int main(int argc, char *argv[])
     8 {
     9     QCoreApplication a(argc, argv);
    10     
    11     {
    12         // test four
    13         {
    14             QObject root;
    15             TestObj *obj = new TestObj(&root);
    16             {
    17                 QList<TestObj*> objList;
    18                 objList.append(obj);
    19             }
    20             qDebug()<<"FOUR: "<<"objList release.";
    21         }
    22 
    23         qDebug()<<"FOUR: "<<"TestObj release? YES!";
    24         qDebug()<<endl;
    25     }
    26     
    27     return a.exec();
    28 }

     运行结果:

    结论: 

    这里的root对象起到了类似智能指针的作用,这也是Qt的一个特性,即在父对象析构的时候,会将其左右子对象析构。(注意:普通C++对象并无此特性))

    5.将QList作为测试对象的parent,然后进行上述测试

     1 // main.cpp
     2 #include <QCoreApplication>
     3 #include <QList>
     4 #include <QSharedPointer>
     5 
     6 #include "testobj.h"
     7 
     8 int main(int argc, char *argv[])
     9 {
    10     QCoreApplication a(argc, argv);
    11     
    12     {
    13         // test five
    14         {
    15             {
    16                 QList<TestObj*> objList;
    17                 TestObj *obj = new TestObj(&objList); // Error: QList<> is NOT a QObject.
    18                 objList.append(obj);
    19             }
    20             qDebug()<<"FIVE: "<<"objList release.";
    21             qDebug()<<"FIVE: "<<"TestObj release? ERROR!";
    22         }
    23 
    24         qDebug()<<endl;
    25     }
    26     
    27     return a.exec();
    28 }

    测试结果:

    1 // 编译错误,因为QList并不继承自QObject,所以不能作为TestObj的parent

    结论:

    // qlist.h

    // qbytearraylist.h

    QList并不是QObject,只是普通的模板类

    6.扩展一下 QList,继承QObject

     1 // testobjlist.h
     2 #ifndef TESTOBJLIST_H
     3 #define TESTOBJLIST_H
     4 
     5 #include <QObject>
     6 #include <QList>
     7 
     8 class TestObj;
     9 
    10 class TestObjList : public QObject, public QList<TestObj*>
    11 {
    12     Q_OBJECT
    13 public:
    14     explicit TestObjList(QObject *parent = 0);
    15     ~TestObjList();
    16 };
    17 
    18 #endif // TESTOBJLIST_H
     1 // testobjlist.cpp
     2 #include "testobjlist.h"
     3 #include "testobj.h"
     4 #include <QDebug>
     5 
     6 TestObjList::TestObjList(QObject *parent) : QObject(parent)
     7 {
     8     qDebug()<<"TestObjList C.tor.";
     9 }
    10 
    11 TestObjList::~TestObjList()
    12 {
    13     qDebug()<<"TestObjList D.tor.";
    14 }

    测试:

     1 // main.cpp
     2 #include <QCoreApplication>
     3 #include <QList>
     4 #include <QSharedPointer>
     5 
     6 #include "testobj.h"
     7 #include "testobjlist.h"
     8 
     9 int main(int argc, char *argv[])
    10 {
    11     QCoreApplication a(argc, argv);
    12     
    13     {
    14         // test six
    15         {
    16             {
    17                 TestObjList objList;
    18                 TestObj *obj = new TestObj(&objList); // Error: QList<> is NOT a QObject.
    19                 objList.append(obj);
    20             }
    21             qDebug()<<"SIX: "<<"objList release.";
    22             qDebug()<<"SIX: "<<"TestObj release? YES!";
    23         }
    24 
    25         qDebug()<<endl;
    26     }
    27     
    28     return a.exec();
    29 }

    测试结果:

    结论:

     TestObjList 释放的时候会释放其内部的对象

    7.附加测试

    1 {
    2         TestObjList objList;
    3         TestObjList list2 = objList; // Error: QObject Q_DISABLE_COPY
    4 }

    结论:

    Qt为了防止开发者出错,将QObject的类拷贝构造函数和赋值操作符都DISABLE了。这样做的好处是,一旦开发者不小心定义了一个QList<QObject>的容器,在添加对象时就会得到一个编译错误,从而避免发生隐式拷贝。

    总结,使用容器类存储对象时,最好使用对象指针类型,如:QList<TestObj*>,而不要使用 QList<TestObj> 这样的定义。建议采用 智能指针QSharedPointer 或 为对象设置parent 的方法来管理内存,避免内存泄露。

  • 相关阅读:
    用C语言编写生成小学四则运算程序
    每周学习报告
    读现代软件工程有感和自我介绍
    第七天
    第五天
    第六天
    作业九:课程总结
    作业四:结对编程项目--四则运算
    psp记录个人项目花费时间
    作业三:代码规范,代码复查
  • 原文地址:https://www.cnblogs.com/pyw0818/p/8039233.html
Copyright © 2020-2023  润新知