Qt中容器类应该如何存储对象(最好使用对象指针类型,如:QList<TestObj*>,而不要使用 QList<TestObj> 这样的定义,建议采用 智能指针QSharedPointer)
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中容器类应该如何存储对象(最好使用对象指针类型,如:QList<TestObj*>,而不要使用 QList<TestObj> 这样的定义,建议采用 智能指针QSharedPointer)的更多相关文章
- Qt中容器类应该如何存储对象(对象加入到容器时会发生拷贝,容器析构时,容器内的对象也会析构)
Qt提供了丰富的容器类型,如:QList.QVector.QMap等等.详细的使用方法可以参考官方文档,网上也有很多示例文章,不过大部分文章的举例都是使用基础类型:如int.QString等.如果我们 ...
- Qt中容器类应该如何存储对象
Qt提供了丰富的容器类型,如:QList.QVector.QMap等等.详细的使用方法可以参考官方文档,网上也有很多示例文章,不过大部分文章的举例都是使用基础类型:如int.QString等.如果我们 ...
- Chrome扩展开发之三——Chrome扩展中的数据本地存储和下载
目录: 0.Chrome扩展开发(Gmail附件管理助手)系列之〇——概述 1.Chrome扩展开发之一——Chrome扩展的文件结构 2.Chrome扩展开发之二——Chrome扩展中脚本的运行机制 ...
- Qt中的键盘事件,以及焦点的设置(比较详细)
Qt键盘事件属于Qt事件系统,所以事件系统中所有规则对按键事件都有效.下面关注点在按键特有的部分: focus 一个拥有焦点(focus)的QWidget才可以接受键盘事件.有输入焦点的窗口是活动窗口 ...
- Qt 智能指针学习(7种指针)
Qt 智能指针学习 转载自:http://blog.csdn.net/dbzhang800/article/details/6403285 从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ ...
- Qt 智能指针学习(7种QT的特有指针)
从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ #include <QApplication> #include <QLabel> int main(int arg ...
- Qt 智能指针学习(7种QT智能指针和4种std智能指针)
从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ #include <QApplication> #include <QLabel> int main(int arg ...
- Qt 智能指针学习
原地址:http://blog.csdn.net/dbzhang800/article/details/6403285 从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ #include & ...
- [转]Qt 智能指针学习
从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ #include <QApplication> #include <QLabel> int main(int arg ...
随机推荐
- 2.linux的增删改查
一.增删改查 1.建立文件和目录 mkdir /tmp/xueying 2.cd 进入的路径 绝对路径:以根目录为其实目录的路径 ...
- PyTorch Tutorials 5 数据并行(选读)
%matplotlib inline 数据并行(选读) Authors: Sung Kim and Jenny Kang 在这个教程里,我们将学习如何使用 DataParallel 来使用多GPU. ...
- 阿里PB级Kubernetes日志平台建设实践
干货分享 | 阿里PB级Kubernetes日志平台建设实践https://www.infoq.cn/article/HiIxh-8o0Lm4b3DWKvph 日志最主要的采集工具是 Agent,在 ...
- Mat转CImage
uchar *pImg=(uchar *)CI.GetBits();//得到CImage数据区地址 ps=img.ptr<uchar>(i); void MatToCImage( Mat ...
- Android插件化(六): OpenAtlasの改写aapt以防止资源ID冲突
Android插件化(六): OpenAtlasの改写aapt以防止资源ID冲突 转 https://www.300168.com/yidong/show-2791.html 核心提示:引言And ...
- ubuntu 12.04 以固定 IP 地址连接网络并配置DNS
sudo vim /etc/network/interfaces auto eth0 iface eth0 inet static address 192.168.2.155 netmask 255. ...
- memcache安装与简单介绍
本文参考自菜鸟教程中的内容. 安装 安装memcache的时候,请切换为root用户 root@centos # wget http://www.memcached.org/files/memcach ...
- APP 抓包-fiddler
App抓包原理 客户端向服务器发起HTTPS请求 抓包工具拦截客户端的请求,伪装成客户端向服务器进行请求 服务器向客户端(实际上是抓包工具)返回服务器的CA证书 抓包工具拦截服务器的响应,获取服务器证 ...
- 【419】C语言语句
判断语句 C 语言提供了以下类型的判断语句.点击链接查看每个语句的细节. 语句 描述 if 语句 一个 if 语句 由一个布尔表达式后跟一个或多个语句组成. if...else 语句 一个 if 语句 ...
- 前端表格插件datatables
下载datatables datatables官网:https://www.datatables.net/ datatables下载地址:https://www.datatables.net/down ...