前言
这学期开了专业课Qt,第一次接触到C++,在C++中学习过程中,我们都知道:
delete 和 new 必须 配对使用(一 一对应):delete少了,则内存泄露,多了麻烦更大。
Qt作为C++的库,显然是不会违背C++的前述原则的。可是:
在Qt中,我们很多时候都疯狂地用new,却很少用delete,缺少的 delete 去哪儿了?!
本文整理了C++中关于delete和new的知识点。
Qt半自动的内存管理
在Qt中,以下情况下你new出的对象你可以不用 亲自去delete (但你应该清楚delete在何处被Qt调用的,怎么被调用的):
QObject及其派生类的对象,如果其parent非0,那么其parent析构时会析构该对象(本文内容围绕这一点展开 )
除此之外,有些类的对象可以接收设置一些特别的标记,比如:
QWidget及其派生类的对象,可以设置 Qt::WA_DeleteOnClose 标志位(当close时会析构该对象)
QAbstractAnimation派生类的对象,可以设置 QAbstractAnimation::DeleteWhenStopped
QRunnable::setAutoDelete()
MediaSource::setAutoDelete()
…
注意:这些用法会有些陷阱 ,请注意看本文最后的3个小例子。
在Qt中,最基础和核心的类是:QObject 。它的魔力很大,本文只关注两点:
- 父子关系
- deleteLater
父子关系
在Qt中,每个 QObject 内部都有一个list,用来保存所有的 children,还有一个指针,保存自己的parent。当它自己析构时,它会将自己从parent的列表中删除,并且析构掉所有的children。
注意:在 Qt 中,我们经常会遇到
- 基类、派生类,或父类、子类。 这是对于派生体系来说的,和在C++相关书中看到的完全一样,与这的parent无关
- 父对象、子对象、父子关系。 这是Qt中所特有的,也就是这儿的parent所引入的,与类的继承关系无关
建立与解除
Q_INVOKABLE QObject::QObject ( QObject * parent = 0 )
创建一个QObject对象时,如果指定了父对象,它就会将自己添加到父对象的 children 列表中
QObject::~QObject () [virtual]
当一个QObject对象析构时,它会将自己从父对象的 children 列表中移除(parent非0的话)
void QObject::setParent ( QObject * parent )
通过该函数,将自己从原父对象的children中删除,添加到新parent的children列表中
注:这三个函数都是通过一个内部私有函数来实现的,这就是
QObjectPrivate::setParent_helper(QObject *o)
获取父、子对象
每个QObject只有一个父对象:
QObject * QObject::parent () const
子对象可以有多个
const QObjectList & QObject::children () const
所以可以根据条件来查找喽:
T QObject::findChild ( const QString & name = QString() ) const
QList QObject::findChildren ( const QString & name = QString() ) const
关于delete的示例程序
- 普通释放(使用delete)和依靠父对象
#include "mainwindow.h"
#include <QApplication>
#include <QtWidgets>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QWidget * widget = new QWidget();
widget->setWindowTitle(QObject::tr("我是widget"));
QLabel * label = new QLabel();
label->setWindowTitle(QObject::tr("我是label"));
label->setText(QObject::tr("我是个窗口"));
label->resize(180,20);
QLabel * label2 = new QLabel(widget);
label2->setText(QObject::tr("我不是独立窗口,只是widget的子部件"));
label2->resize(250,20);
label->show();
widget->show();
int ret = a.exec();
delete label; // 释放内存,因为label没有父对象,所以它的释放只能自己来。
delete widget; // label2有父对象,它依靠父对象释放,不用再单独对其释放
return ret;
}
- 使用标志位
----使用标志位可以不显示的使用delete,但和使用delete是一样的。因为Qt在暗中
使用了delete。
#include <QApplication>
#include <QLabel>
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QLabel *label = new QLabel;
label->setAttribute(Qt::WA_DeleteOnClose);
label->setText("first line\nsecond line");
label->show();
return app.exec();
}
----下面是一个暗中使用delete的例子。这是一个类,它有一个成员函数,可以删除
自己。当然只能删除由关键字new创建的自己。
#include<iostream>
using namespace std;
//类AAA
class AAA
{
public:
//构造函数
AAA();
~AAA(void);
void delete_me(void);
};
//构造函数
AAA::AAA(void)
{
//空的
}
//析构函数
AAA::~AAA(void)
{
//空的
}
//删除自己
void AAA::delete_me(void)
{
delete this;
}
//主函数
int main(void)
{
AAA *a = new AAA;
cout<<"point a will be deleted by himself."<<endl;
a->delete_me();
cout<<"point a has been deleted."<<endl;
return 0;
}