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)的更多相关文章

  1. Qt中容器类应该如何存储对象(对象加入到容器时会发生拷贝,容器析构时,容器内的对象也会析构)

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

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

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

  3. Chrome扩展开发之三——Chrome扩展中的数据本地存储和下载

    目录: 0.Chrome扩展开发(Gmail附件管理助手)系列之〇——概述 1.Chrome扩展开发之一——Chrome扩展的文件结构 2.Chrome扩展开发之二——Chrome扩展中脚本的运行机制 ...

  4. Qt中的键盘事件,以及焦点的设置(比较详细)

    Qt键盘事件属于Qt事件系统,所以事件系统中所有规则对按键事件都有效.下面关注点在按键特有的部分: focus 一个拥有焦点(focus)的QWidget才可以接受键盘事件.有输入焦点的窗口是活动窗口 ...

  5. Qt 智能指针学习(7种指针)

    Qt 智能指针学习 转载自:http://blog.csdn.net/dbzhang800/article/details/6403285 从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ ...

  6. Qt 智能指针学习(7种QT的特有指针)

    从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ #include <QApplication> #include <QLabel> int main(int arg ...

  7. Qt 智能指针学习(7种QT智能指针和4种std智能指针)

    从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ #include <QApplication> #include <QLabel> int main(int arg ...

  8. Qt 智能指针学习

    原地址:http://blog.csdn.net/dbzhang800/article/details/6403285 从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ #include & ...

  9. [转]Qt 智能指针学习

    从内存泄露开始? 很简单的入门程序,应该比较熟悉吧 ^_^ #include <QApplication> #include <QLabel> int main(int arg ...

随机推荐

  1. oracle 导入导出功能

    关于expdp和impdp 使用EXPDP和IMPDP时应该注意的事项: EXP和IMP是客户端工具程序,它们既可以在客户端使用,也可以在服务端使用. - EXPDP和IMPDP是服务端的工具程序,他 ...

  2. Java 面向对象(十三)

    异常 什么是异常 异常 :指的是程序在执行过程中,出现的非正常的情况,最终会导致JVM的非正常停止. 在Java等面向对象的编程语言中,异常本身是一个类,产生异常就是创建异常对象并抛出了一个异常对象. ...

  3. js回车键事件

    js回车键事件 一.总结 一句话总结: $("#focus").keypress(function(event){if(event.which === 13) { /*点击回车要执 ...

  4. Python 死循环

    while True: try: x=int(input("Please enter a number:")) break except ValueError: print(&qu ...

  5. Java同步数据结构之ConcurrentHashMap

    前言 这是Java并发包最后一个集合框架的数据结构,其复杂程度也较以往任何数据结构复杂的多,顾名思义ConcurrentHashMap是线程安全版本的HashMap,总所周知HashMap是非线程安全 ...

  6. 阶段5 3.微服务项目【学成在线】_day09 课程预览 Eureka Feign_07-Feign远程调用-Feign测试

    2.2.1 Feign介绍 Feign是Netflix公司开源的轻量级rest客户端,使用Feign可以非常方便的实现Http 客户端.Spring Cloud引入 Feign并且集成了Ribbon实 ...

  7. PAT 甲级 1035 Password (20 分)(简单题)

    1035 Password (20 分)   To prepare for PAT, the judge sometimes has to generate random passwords for ...

  8. C#关于DateTime得到的当前时间的格式和用法

    DateTime.Now.ToShortTimeString() DateTime dt = DateTime.Now; dt.ToString();//2005-11-5 13:21:25 dt.T ...

  9. 完全解读 margin 标签

    你真的了解margin吗?你知道margin有什么特性吗?你知道什么是垂直外边距合并?margin在块元素.内联元素中的区别?什么时候该用 padding而不是margin?你知道负margin吗?你 ...

  10. jQuery跨域调用WebService

    jQuery跨域调用WebService举例html: <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN&qu ...