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 的方法来管理内存,避免内存泄露。

posted @ 2017-12-14 18:44  Day_Dreamer  阅读(4335)  评论(0编辑  收藏  举报