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 的方法来管理内存,避免内存泄露。
https://www.cnblogs.com/pyw0818/p/8039233.html
Qt中容器类应该如何存储对象(对象加入到容器时会发生拷贝,容器析构时,容器内的对象也会析构)的更多相关文章
- Qt中容器类应该如何存储对象
Qt提供了丰富的容器类型,如:QList.QVector.QMap等等.详细的使用方法可以参考官方文档,网上也有很多示例文章,不过大部分文章的举例都是使用基础类型:如int.QString等.如果我们 ...
- Qt中容器类应该如何存储对象(最好使用对象指针类型,如:QList<TestObj*>,而不要使用 QList<TestObj> 这样的定义,建议采用 智能指针QSharedPointer)
Qt提供了丰富的容器类型,如:QList.QVector.QMap等等.详细的使用方法可以参考官方文档,网上也有很多示例文章,不过大部分文章的举例都是使用基础类型:如int.QString等.如果我们 ...
- c++ 容器反转并且拷贝到一个新容器中
// reverse_copy example #include <iostream> // cout #include <algorithm> // reverse_copy ...
- javascript中的内置对象总结
内置对象 标准内置对象 Object Object.create Object.prototype.toString Object.prototype.hasOwnProperty Boolean S ...
- JSP中的Java代码和内置对象
一.JSP中的Java代码 (一)JSP页面中有三种方式嵌入java代码: 1.java的表达式 格式:<%= java表达式 %> 2.java的语句 格式:<% java语句&g ...
- Webform(七)——内置对象(Session、Application)和Repeater的Command操作
内置对象:用于页面之间的数据交互 为什么要使用这么内置对象?因为HTTP的无状态性. 一.内置对象 (一)Session 跟Cookies一样用来存储用户数据 1.Session.Cookies对比 ...
- C# WebForm内置对象2+Repeater的Command
内置对象:用于页面之间的数据交互 为什么要使用这么内置对象?因为HTTP的无状态性. Session:在计算机中,尤其是在网络应用中,称为“会话控制”.Session 对象存储特定用户会话所需的属性及 ...
- Java EE JSP内置对象及表达式语言
一.JSP内置对象 JSP根据Servlet API规范提供了一些内置对象,开发者不用事先声明就可使用标准变量来访问这些对象. JSP提供了9种内置对象: (一).request 简述: JSP编程中 ...
- python django中使用sqlite3数据库 存储二进制数据ByteArray
在python中使用sqlite3数据库存储二进制流数据ByteArray,在django使用sqlite3数据库时,有时候也要注意最好使用二进制流ByteArray插入字符串. 使用ByteArra ...
随机推荐
- Robot Framework配置发送邮件功能
请参考:http://www.robotframework.net/?/article/118
- 查找被占用的端口的服务并kill掉
转自:http://blog.csdn.net/gsls200808/article/details/52456136 方法: C:\>netstat -ano|findstr 8000 TCP ...
- BootStrap Table将时间戳更改为日期格式
一.使用BootStrap Table遇到的问题: 1.MyBatis从数据库中取出的时间格式如下:2017-12-04 21:43:19.0,时间后面多了一个点零. 2.从BootStrap Tab ...
- Ansible的Inventory管理
Ansible将可管理的服务器集合成为Inventory,Inventory的管理便是服务器的管理. hosts文件的位置: /etc/ansible/hosts 在命令行通过-i参数指定 通过/et ...
- [Ubuntu] apt 添加第三方库
1. 方法一:直接在 /etc/apt/sources.list 添加第三方库. $ sudo vi /etc/apt/sources.list 在其中添加: deb http://archive.s ...
- NUC972配置为支持NFS
系统平台:virtualbox3.2.10+ubuntu10.10 安装nfs: #sudo apt-get install nfs-kernel-server ubuntu10.10中的已经是最新版 ...
- Linux线程编程之生产者消费者问题
前言 本文基于顺序循环队列,给出Linux生产者/消费者问题的多线程示例,并讨论编程时需要注意的事项.文中涉及的代码运行环境如下: 本文假定读者已具备线程同步的基础知识. 一 顺序表循环队列 1.1 ...
- poj_1042 贪心算法
poj 1042 gone fishing 题目要求: 由有n个湖, 按照顺序排列,一个人从第一个湖向最后一个湖行进(方向只能从湖0到湖n-1),途中可以在湖中钓鱼.在每个湖中钓鱼时,开始的5分钟内可 ...
- delphi 10 Seattle 第一个Android程序
delphi 10 Seattle 第一个Android程序 1.打开Delphi RAD Studio Seattle,如下图 2.选择black application 点击OK 3. ...
- 【基础】java类的各种成员初始化顺序
父子类继承时的静态代码块,普通代码块,静态方法,构造方法,等先后顺序 前言: 普通代码块:在方法或语句中出现的{}就称为普通代码块.普通代码块和一般的语句执行顺序由他们在代码中出现的次序决定--“先出 ...