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

https://www.cnblogs.com/pyw0818/p/8039233.html

Qt中容器类应该如何存储对象(对象加入到容器时会发生拷贝,容器析构时,容器内的对象也会析构)的更多相关文章

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

    Qt提供了丰富的容器类型,如:QList.QVector.QMap等等.详细的使用方法可以参考官方文档,网上也有很多示例文章,不过大部分文章的举例都是使用基础类型:如int.QString等.如果我们 ...

  2. Qt中容器类应该如何存储对象(最好使用对象指针类型,如:QList<TestObj*>,而不要使用 QList<TestObj> 这样的定义,建议采用 智能指针QSharedPointer)

    Qt提供了丰富的容器类型,如:QList.QVector.QMap等等.详细的使用方法可以参考官方文档,网上也有很多示例文章,不过大部分文章的举例都是使用基础类型:如int.QString等.如果我们 ...

  3. c++ 容器反转并且拷贝到一个新容器中

    // reverse_copy example #include <iostream> // cout #include <algorithm> // reverse_copy ...

  4. javascript中的内置对象总结

    内置对象 标准内置对象 Object Object.create Object.prototype.toString Object.prototype.hasOwnProperty Boolean S ...

  5. JSP中的Java代码和内置对象

    一.JSP中的Java代码 (一)JSP页面中有三种方式嵌入java代码: 1.java的表达式 格式:<%= java表达式 %> 2.java的语句 格式:<% java语句&g ...

  6. Webform(七)——内置对象(Session、Application)和Repeater的Command操作

    内置对象:用于页面之间的数据交互 为什么要使用这么内置对象?因为HTTP的无状态性. 一.内置对象 (一)Session 跟Cookies一样用来存储用户数据 1.Session.Cookies对比 ...

  7. C# WebForm内置对象2+Repeater的Command

    内置对象:用于页面之间的数据交互 为什么要使用这么内置对象?因为HTTP的无状态性. Session:在计算机中,尤其是在网络应用中,称为“会话控制”.Session 对象存储特定用户会话所需的属性及 ...

  8. Java EE JSP内置对象及表达式语言

    一.JSP内置对象 JSP根据Servlet API规范提供了一些内置对象,开发者不用事先声明就可使用标准变量来访问这些对象. JSP提供了9种内置对象: (一).request 简述: JSP编程中 ...

  9. python django中使用sqlite3数据库 存储二进制数据ByteArray

    在python中使用sqlite3数据库存储二进制流数据ByteArray,在django使用sqlite3数据库时,有时候也要注意最好使用二进制流ByteArray插入字符串. 使用ByteArra ...

随机推荐

  1. Struts2开发模式漏洞

    当Struts2中的devMode模式设置为true时,存在严重远程代码执行漏洞.如果WEB服务以最高权限运行时,可远程执行任意命令,包括远程控制服务器. 如果为受影响的版本,建议修改配置文件stru ...

  2. RF变量列表类型@{}和${}列表类型的关系

    总结:@{}列表类型和${}列表类型都可以表示list类型,均可以通过 set variable 和 create list 创建,区别主要是展示格式和引用格式: @{}类型可以通过 set vari ...

  3. Expected BEGIN_OBJECT but was BEGIN_ARRAY at line 1 column 2

    原因是解析的时候多了,逗号,或是 \ 解决方法:一 revJson=revJson.replace("\\", "");//去掉'/' revJson=revJ ...

  4. WP8.1学习系列(第二章)——Toast通知

    Toast 通知概述(Windows 运行时应用) 你的应用要想通过 Toast 通知通信,必须在应用的清单文件中声明它支持 Toast.Toast 通知可包含文本,并且 Windows 上的 Toa ...

  5. 【cs229-Lecture3】Logistic回归

    参考: http://www.itongji.cn/article/12112cH013.html http://blog.csdn.net/zouxy09/article/details/20319 ...

  6. springbatch---->springbatch的使用(四)

    这里我们重点学习一下springbatch里面的各种监听器的使用,以及job参数的传递.追求得到之日即其终止之时,寻觅的过程亦即失去的过程. springbatch的监听器 一.JOB LISTENE ...

  7. 【线程】Thread中的join介绍

    因为sleep.wait.join等阻塞,可以使用interrupted exception异常唤醒. 一.作用 Thread类中的join方法的主要作用就是同步,它可以使得线程之间的并行执行变为串行 ...

  8. 【Python系列】Python3获取控制台输入

    """ 接收控制台的输入 How old are you? 18 How tall are you ? 180 How much do you weigh? 50 So ...

  9. jQuery属性操作(二)

    挂载到$上的几个属性操作方法分析,发现属性操作用到了sizzle封装的方法 attr: function( elem, name, value ) {        var hooks, ret,   ...

  10. Why is IMAP better than POP?

    https://www.fastmail.com/help/technical/imapvspop.html POP is a very simple protocol that only allow ...