Qt提供了丰富的容器类型,如:QList、QVector、QMap等等。详细的使用方法可以参考官方文档,网上也有很多示例文章,不过大部分文章的举例都是使用基础类型:如int、QString等。如果我们要存储一个对象类型,应该如何做呢?—— 当然是和int类型一样操作,因为这些容器类都是泛型的。不过,我们今天要讨论的不是容器类的使用用法,而是容器存储的对象内存如何释放的问题。

(这里提到了对象类型是指 Class/Struct,可以继承自QObject,也可以是普通的C++类。)

下面以QList<T>为例,直接通过代码来看一下以下几种情况的内存释放。

0.测试前的一点点准备

 // testobj.h
#ifndef TESTOBJ_H
#define TESTOBJ_H #include <QObject> // 测试对象(也可以不继承QObject)
class TestObj : public QObject
{
Q_OBJECT
public:
explicit TestObj(QObject *parent = ); ~TestObj(); TestObj(const TestObj& obj); TestObj& operator=(const TestObj& obj); }; #endif // TESTOBJ_H

实现TestObj

 // testobj.cpp
#include "testobj.h"
#include <QDebug> // 构造时输出log
TestObj::TestObj(QObject *parent) : QObject(parent)
{
qDebug()<<"TestObj C.tor.";
} // 析构时输出log
TestObj::~TestObj(){
qDebug()<<"TestObj D.tor.";
} // 拷贝时输出log
TestObj::TestObj(const TestObj& obj){ qDebug()<<"TestObj COPY.";
} // 赋值时输出log
TestObj& TestObj::operator=(const TestObj& obj){ qDebug()<<"TestObj =.";
return *this;
}

1.在栈上创建对象,然后添加容器

 // main.cpp
#include <QCoreApplication>
#include <QList> #include "testobj.h" int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); {
// Test one
{
TestObj obj;
{
QList<TestObj> objList;
objList.append(obj);
} qDebug()<<"ONE: "<<"objList release.";
} qDebug()<<"ONE: "<<"TestObj release.";
qDebug()<<endl;
} return a.exec();
}

运行结果:

结论:

对象加入到容器时会发生拷贝,容器析构时,容器内的对象也会析构。

2. 在堆上创建对象,然后添加到容器 

 // main.cpp
#include <QCoreApplication>
#include <QList> #include "testobj.h" int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); {
// test tow
{
TestObj *obj = new TestObj;
{
QList<TestObj*> objList;
objList.append(obj);
}
qDebug()<<"TWO: "<<"objList release.";
} qDebug()<<"TWO: "<<"TestObj release? NO!";
qDebug()<<endl;
} return a.exec();
}

运行结果:

结论:

对象不会发生拷贝,但容器析构后容器内的对象并未析构

3. 使用Qt智能指针来管理堆上的对象,然后添加到容器

 // main.cpp
#include <QCoreApplication>
#include <QList>
#include <QSharedPointer> #include "testobj.h" int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); {
// test three
{
QSharedPointer<TestObj> obj(new TestObj);
{
QList<QSharedPointer<TestObj>> objList;
objList.append(obj);
}
qDebug()<<"THREE: "<<"objList release.";
} qDebug()<<"THREE: "<<"TestObj release? YES!";
qDebug()<<endl;
} return a.exec();
}

运行结果:

结论:

对象不会发生拷贝,容器析构的时候,容器内对象并未析构,但超过作用域后,智能指针管理的对象会析构。

4.给测试对象一个parent(父对象),然后进行上述测试

 // main.cpp
#include <QCoreApplication>
#include <QList> #include "testobj.h" int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); {
// test four
{
QObject root;
TestObj *obj = new TestObj(&root);
{
QList<TestObj*> objList;
objList.append(obj);
}
qDebug()<<"FOUR: "<<"objList release.";
} qDebug()<<"FOUR: "<<"TestObj release? YES!";
qDebug()<<endl;
} return a.exec();
}

运行结果:

结论:

这里的root对象起到了类似智能指针的作用,这也是Qt的一个特性,即在父对象析构的时候,会将其左右子对象析构。(注意:普通C++对象并无此特性))

5.将QList作为测试对象的parent,然后进行上述测试

 // main.cpp
#include <QCoreApplication>
#include <QList>
#include <QSharedPointer> #include "testobj.h" int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); {
// test five
{
{
QList<TestObj*> objList;
TestObj *obj = new TestObj(&objList); // Error: QList<> is NOT a QObject.
objList.append(obj);
}
qDebug()<<"FIVE: "<<"objList release.";
qDebug()<<"FIVE: "<<"TestObj release? ERROR!";
} qDebug()<<endl;
} return a.exec();
}

测试结果:

 // 编译错误,因为QList并不继承自QObject,所以不能作为TestObj的parent

结论:

// qlist.h

// qbytearraylist.h

QList并不是QObject,只是普通的模板类

6.扩展一下 QList,继承QObject

 // testobjlist.h
#ifndef TESTOBJLIST_H
#define TESTOBJLIST_H #include <QObject>
#include <QList> class TestObj; class TestObjList : public QObject, public QList<TestObj*>
{
Q_OBJECT
public:
explicit TestObjList(QObject *parent = );
~TestObjList();
}; #endif // TESTOBJLIST_H
 // testobjlist.cpp
#include "testobjlist.h"
#include "testobj.h"
#include <QDebug> TestObjList::TestObjList(QObject *parent) : QObject(parent)
{
qDebug()<<"TestObjList C.tor.";
} TestObjList::~TestObjList()
{
qDebug()<<"TestObjList D.tor.";
}

测试:

 // main.cpp
#include <QCoreApplication>
#include <QList>
#include <QSharedPointer> #include "testobj.h"
#include "testobjlist.h" int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv); {
// test six
{
{
TestObjList objList;
TestObj *obj = new TestObj(&objList); // Error: QList<> is NOT a QObject.
objList.append(obj);
}
qDebug()<<"SIX: "<<"objList release.";
qDebug()<<"SIX: "<<"TestObj release? YES!";
} qDebug()<<endl;
} return a.exec();
}

测试结果:

结论:

TestObjList 释放的时候会释放其内部的对象

7.附加测试

 {
TestObjList objList;
TestObjList list2 = objList; // Error: QObject Q_DISABLE_COPY
}

结论:

Qt为了防止开发者出错,将QObject的类拷贝构造函数和赋值操作符都DISABLE了。这样做的好处是,一旦开发者不小心定义了一个QList<QObject>的容器,在添加对象时就会得到一个编译错误,从而避免发生隐式拷贝。

总结,使用容器类存储对象时,最好使用对象指针类型,如:QList<TestObj*>,而不要使用 QList<TestObj> 这样的定义。建议采用 智能指针QSharedPointer 或 为对象设置parent 的方法来管理内存,避免内存泄露。

Qt中容器类应该如何存储对象的更多相关文章

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

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

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

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

  3. Qt中的常用容器类(解释比较全面,有插图)

    在Qt库中为我们提供了一系列的基于模板的容器类.这些类可以被用来存储特定类型的项.例如,如果你需要一个大小可以变得QString数组,那么可以使用QVector<QString>. 这些容 ...

  4. 【Java】链表中存储对象的问题

    更新: 在一次搜索“变量声明在循环体内还是循环体外”问题时,碰见了一个这样的代码,与本文类似,代码如下: Document [] old ......//这是数据源 EntityDocument[] ...

  5. Qt中如何根据类名来实例化对象

    对于Qt 来说,是可以做到运行时,根据对象的类名字(字符串)来获得对象的实例的,这点和一些语言的反射机制是一样的. 但是在Qt中,我们需要所额外的一步,就是注册.只要做到了注册,我们就可以 自由的创建 ...

  6. COM结构化存储中存储对象或者流对象的命名规则

      COM结构化存储中存储对象或者流对象的命名规则

  7. QT之在QML中使用C++类和对象

    QML其实是对ECMAScript的扩展,融合了Qt object系统,它是一种新的解释性语言,QML引擎虽然由Qt C++实现,但QML对象的运行环境说到底和C++对象的上下文环境是不通的,是平行的 ...

  8. qt中使用sqlite存储数据

    一.sqilte的安装 在Windows上安装SQLite: 请访问 SQLite 下载页面,从 Windows 区下载预编译的二进制文件. 您需要下载 sqlite-tools-win32-*.zi ...

  9. Qt 学习之路:存储容器

    存储容器(containers)有时候也被称为集合(collections),是能够在内存中存储其它特定类型的对象,通常是一些常用的数据结构,一般是通用模板类的形式.C++ 提供了一套完整的解决方案, ...

随机推荐

  1. Kafka个人总结

    Kafka 应对场景:消息持久化.吞吐量是第一要求.状态由客户端维护.必须是分布式的.Kafka 认为 broker 不应该阻塞生产者,高效的磁盘顺序读写能够和网络 IO 一样快,同时依赖现代 OS ...

  2. iOS笔记,开发经验总结【持续更新】

    1. 设置navigationBar 背景颜色有色差, 原因:如果单纯的设置背景颜色也是有高斯模糊处理的效果,对纯色高斯模糊处理过后相当于纯色的70%(猜测)透明化处理,但是反正就是有色差 解决方法一 ...

  3. 谈谈对html5的了解

    1.良好的移动性,以移动端设备为主. 2.响应式设计,以适应自动变化的屏幕尺寸. 3.支持离线缓存技术,webStorage本地缓存. 4.新增canvas,video,audio等新.标签元素.新增 ...

  4. css布局-内容自适应屏幕

    css页面布局,实现内容部分自适应屏幕,当内容高度小于浏览器窗口高度时,页脚在浏览器窗口底部:当内容高度高于浏览器窗口高度时,页脚自动被撑到页面底部. <style type="tex ...

  5. 算法训练 K好数(C/C++)AC码

    蓝桥杯 算法训练 K好数 AC码 题目要求: 算法训练 K好数 问题描述 如果一个自然数N的K进制表示中任意的相邻的两位都不是相邻的数字,那么我们就说这个数是K好数.求L位K进制数中K好数的数目.例如 ...

  6. 微信小程序 微信支付

    微信小程序前端自处理: //时间戳 timeStamp() { return parseInt(new Date().getTime() / 1000) + '' }, //随机数 randomStr ...

  7. SSM(Spring5.x+Mybatis3)框架搭建【解决日志问题】(Github源码)

    闲来无事,用SSM写个小东西,发现spring已经迭代到5.x了,遂出此文,希望对各位同学有些许帮助. IDE:idea OS:windows 源代码:https://github.com/JHeav ...

  8. Python之路(六)---> 函数、变量

    Python中的函数和数学上的函数定义是不一样的,从数学的角度上来说函数的定义:给定一个数集A,假设其中的元素为x.现对A中的元素x施加对应法则f,记作f(x),得到另一数集B.假设B中的元素为y.则 ...

  9. HyperLedger Fabric 1.4 区块链技术原理(2.2)

    区块链从字面上理解:数据记录在区块中,通过一定的算法把区块连成一个链.       区块链通过哈希(Hash)算法,生成一串字符串,保存在区块的头部中,一个的区块通过指向上一个Hash值,加入到区块链 ...

  10. 20155301 2016-2017-2 《Java程序设计》第1周学习总结

    20155301 2016-2017-2 <Java程序设计>第1周学习总结 教材学习内容总结 Java SE的全称是Java Platform, Standard Edition,并用于 ...