在Qt的程序中经常会看到只有new而不delete的情况,其实是因为Qt有一套回收内存的机制,主要的规则如下:
1.所有继承自QObject类的类,如果在new的时候指定了父亲,那么它的清理时在父亲被delete的时候delete的,所以如果一个程序中,所有的QObject类都指定了父亲,那么他们是会一级级的在最上面的父亲清理时被清理,而不用自己清理;
2.程序通常最上层会有一个根的
QObject,就是放在setCentralWidget()中的那个QObject,这个QObject在
new的时候不必指定它的父亲,因为这个语句将设定它的父亲为总的QApplication,当整个QApplication没有时它就自动清理,所以也
无需清理。这里Qt4和Qt3有不同,Qt3中用的是setmainwidget函数,但是这个函数不作为里面QObject的父亲,所以Qt3中这个顶
层的QObject要自行销毁)。
3.这是有人可能会问那如果我自行delete
掉这些Qt接管负责销毁的指针了会出现什么情况呢,如果这么做的话,正常情况下被delete的对象的父亲会知道这件事情,它会知道它的儿子被你直接
delete了,这样它会将这个儿子移出它的列表,并且重新构建显示内容,但是直接这样做是有风险的!也就是要说的下一条。
4.当一个QObject正在接受事件队列时如果中途被你DELETE掉了,就是出现问题了,所以Qt中建议大家不要直接DELETE掉一个QObject,如果一定要这样做,要使用QObject的deleteLater()函数,它会让所有事件都发送完一切处理好后马上清除这片内存,而且就算调用多次的deletelater也不会有问题。
5.Qt不建议在一个QObject对象的父亲
的范围之外持有对这个对象的指针,因为如果这样外面的指针很可能不会察觉这个QObject被释放,会出现错误。如果一定要这样,就要记住你在哪这样做
了,然后抓住那个被你违规使用的QObject的destroyed()信号,当它没有时赶快置零你的外部指针。当然我认为这样做是及其麻烦也不符合高效
率编程规范的,所以如果要这样在外部持有QObject的指针,建议使用引用或者用智能指针,如Qt就提供了智能指针针对这些情况,见最后一条。
6.Qt中的智能指针封装为QPointer类,所有QObject的子类都可以用这个智能指针来包装,很多用法与普通指针一样,可以详见Qt
assistant
通过调查这个Qt的内存管理功能,发现了很多东西,现在觉得虽然这个Qt弄的有点小复杂,但是使用起来还是很方便的,最后要说的是某些内存泄露的检测工具会认为Qt的程序因为这种方式存在内存泄露,发现时大可不必理会~
Qt: 释放窗口资源
1.
对于使用指针,使用new创建的窗口,当然可以使用delete显示的释放其占用的资源:
Widget *w = new Widget();
delete w;
2.
对于使用指针,使用new创建的窗口,还可以使用QWidget::setAttribute方法来当窗口关闭后自动释放其占用的资源,而不用户显示的去调用delete释放,此方法当然也会调用窗口的析构函数:
Widget *w = new Widget();
w->setAttribute(Qt::WA_DeleteOnClose);
这可以用于非模态对话框,因为非模态对话框如果是用指针形式创建,但是再接着delete的话,窗口就没了,如果不使用delete释放窗口占用的资源,
又会赞成泄漏。如果使用普通变量创建,同样的也因为变量作用域马上就结束而窗口也没了,另一种方法就是使用多线程,不过这个的代价大了点。所以这种技术在
创建非模态对话框上是非常典型的运用。
测试方式:在Widget中分配大量的内存,显示与关闭多个此类窗口,看看任务管理器里此程序的内存变化情况,是否真正的释放了占用的内存(当然释放了)。在C++中使用new分配内存时,如array
= new
double[length],此时,给array的内存实际上并没有真正的分配,必须等到第一次使用这些内存后才会真正地为其分配物理内存,如:memset(array,
1, length * sizeof(double))
3. 窗口的内存管理交给父Widget:
Widget *w = new Widget(parent);
但这时,如果父Widget不结束,这个窗口的资源一直会占用着。
至于使用哪种技术来释放窗口的资源,要看具体的运用时,哪种方式更合适。
|
使用QT进行内存管理
Memory Management with Qt
使用QT进行内存管理
Garbage Collection through the Back Door
通过后门垃圾回收
Highly typed programming languages demand explicit (or at least
implicit) creation of objects mentioning their types. They differ
in how the objects are deleted: there are two approaches - direct
deletion by the program or garbage collection by the
language.
高级类型的语言要求对象创建时明确的(或者至少是隐式的)声明他们的类型。他们可以通过对象是如何销毁的来区别:有两种方法——通过程序直接销毁或者通过语言的垃圾回收机制。
Explicitly deleting languages (eg. C++, Pascal) demand that the
user knows when it is best to get rid of a dynamically created
object. Garbage collected languages (eg. Java, SmallTalk) try to
find out when an object is no longer used and automatically reclaim
the memory.
明确的删除的语言(如,C++,Pascal)要求用户知道什么时候最好避免动态创建的对象。垃圾回收的语言(如java,Smaltalk)尝试找出什么时候一个对象不再被使用并自动的回收那部分的内存。
Explicit deletion has the nice property that the programmer is in
full control over the programs ressources - the amount of memory
used and the amount and timing of CPU cycles spent on managing
these ressources.
明确的删除的好处那就是程序员可以完全控制程序或者资源——使用的内存的大小和管理这些资源花费的cpu周期的大小和数量。
Garbage Collection on the other hand takes a lot of responsibility
off the programmer by keeping track on object references inside the
system and freeing memory as needed.
另一方面垃圾回收通过在系统内部维持对象的属性的轨迹并且在必要时释放内存来分担程序员的一部分的职责
Default C++ Memory Handling
默认的C++的内存处理
C++ is a language that demands that the user deletes objects
explicitly. There are three important strategies on how to handle
this:
C++是一种要求用户显式的删除对象的语言。在如何处理方面有三种重要的策略:
Let the creating object delete its child
objects.
Let the last object to handle an object delete
it.
Don't care about memory and forget about
it.
让创建的对象删除它的子对象。
让最后的对象来控制一个对象来删除它。
不关心内存并且忘了有这件事。
The last strategy is called "memory leak" and usually regarded as a
"bug".
最后的策略被称为内存泄露并且常常被认为是一个‘bug’
The real problem with C++ is to find out which of the first two
strategies is right and how to implement it. There are some cases
in which the creating object is deleted much earlier than the child
object and in which it is hard to find out which is the last object
to handle it.
C++中的真正的问题是找出前两种策略的哪个是正确的并且如何实现它。在一些情况中创建的对象比子对象删除的早的多并且很难找出哪个是最后的对象来处理它
Default Qt Memory Handling
默认的QT内存处理
Qt maintains hierarchies of objects. For widgets (visible elements)
these hierarchies represent the stacking order of the widgets
themselves, for non-widgets they merely express "ownership" of
objects. Qt deletes child objects together with their parent
object. While this takes care of 90% of memory handling problems it
also brings some challenges and leaves a few aspects
open.
Qt维护了对象的分类。对widget(可视化元件)这些层次表现在widgets的压栈顺序,对非widget的他们很少表现出对象的归属。Qt把子对象和他们的父对象一起删除。这样能够处理90%的内存处理问题同时它也带来了一些机会并且一些方面的敞开。
QPointer
QPointer is a template class that can watch a dynamic object and
updates when the object is deleted, otherwise it behaves just like
a normal pointer:
Qpointer是一个模版类,它能够监控动态对象并且在对象被删除时更新,否则它和一般的指针的表现几乎是一样的。
QDate *mydate=new QDate(QDate::currentDate());
QPointer
mypointer=mydata;
mydate->year();
// -> 2005
mypointer->year(); // -> 2005
After deletion of the original object it starts to behave
differently:
在删了原对象之后他就开始有不同的表现了
delete mydate;
if(mydate==0) printf("clean pointer");
else printf("dangling pointer");
// -> "dangling pointer"
if(mypointer.isNull()) printf("clean pointer");
else printf("dangling pointer");
// -> clean pointer
QObjectCleanupHandler
The cleanup handler of Qt is a further step towards automatic
garbage collection. It can register several child objects and
delete them all simultaniously when it itself is deleted. It also
recognises automatically if any of its registered objects is
deleted earlier and removes it from its
list.
Qt中的cleanup句柄是自动垃圾回收中走的更远的一步。它能够注册多个的子对象并且在他被删除的同时删除它们。它同样可以自动的发现任何他的注册的对象被提前删除并且从他的表中删除。
This class can be used any time when objects that are not in the
same hierarchy need to be deleted upon a certain event (eg. several
top level windows need to be closed when a button is pressed or
when another window closes).
这个类可以在不在同一层次的对象因为一个特殊的事件(如当一个按钮被按下或者当另一个窗口被关闭的时候,几个顶层的窗口需要被关闭)被删除的任何时候使用.
//instantiate handler
QObjectCleanupHandler *cleaner=new QObjectCleanupHandler;
//create some windows
QPushButton *w;
w=new QPushButton("Remove Me");
w->show();
//register first button
cleaner->add(w);
//if first button is pressed, it removes itself
connect(w,SIGNAL(clicked()),w,SLOT(deleteLater()));
//create second button with no function
w=new QPushButton("Nothing");
cleaner->add(w);
w->show();
//create third button to delete all
w=new QPushButton("Remove All");
cleaner->add(w);
connect(w,SIGNAL(clicked()),cleaner,SLOT(deleteLater()));
w->show();
In the code above three windows containing a single button are
created. If the first button ("Remove Me") is pressed, it will
delete itself (via the "deleteLater" slot) and the cleanup handler
will automatically remove it from its list. If the third button
("Remove All") is pressed it will delete the cleanup handler, which
will in turn delete all buttons that are still
open.
在上面的三个包含了一个简单的窗体代码被创建。如果第一个按钮("Remove
Me")被按下的时候,它将会删除自己(经由"deleteLater"槽)并且cleanup句柄将自动的把它从他的表中删除。如果第三个按钮("Remove
All")被按下的时候,它将删除cleanup句柄,这将依次删除所有还是开着的按钮。
Garbage Collection with Qt
用Qt进行垃圾回收
Sometimes objects are handed around to different other objects
which all jointly own this object - so it is hard to determine when
to delete this object. Fortunately with Qt it is possible to
emulate garbage collection for child and parent classes that are
derived from QObject.
有时候对象被传递给了不同的拥有这个对象的其它对象,所以很难决定什么时候删除这个对象。幸运的是使用Qt,模拟为源自QObject的父子类垃圾回收将是可能的。
There are several approaches to garbage collection. The easiest way
is to implement instances counters, or one could store all owning
objects. The garbage collection could be added to the class itself
or handled by an outside object. Several of these methods will be
developed below.
有几种方法可以进行垃圾回收。最简单的方法是实现实例计数器,或者一个可以存放所拥有的对象。垃圾回收可以被加到一个类自身中或者被一个外部的对象处理。下面是这些方法的实现的几个例子。
Instance Counting
实例计数
Instance counting is the easiest way of implementing garbage
collection. For each reference made to the object the counter is
increased, for each release it is
decreased:
实例计数是实现垃圾回收的最简单的方法。对每个声明,将会使该对象的计数器增加,每个释放将使其减小。
class CountedObject
{
public:
CountedObject(){ctr=0;}
void attach(){ctr++;}
void detach(){
ctr--;
if(ctr<=0)delete this;
}
private:
int ctr;
};
Each object that takes ownership of the object needs to call attach
when it takes ownership and detach when it releases it. So far this
approach does not take advantage of Qt - a more automatic approach
would be this:
在对象他得到所有权时,每个拥有该对象的对象需要调用attach,并且在释放的时候要调用detach。到目前为止这种方法没有使用qt的先进性,更为自动的方法应该是这样的:
class CountedObject:public QObject
{
public:
CountedObject(){ctr=0;}
void attach(QObject*o){
ctr++;
connect(o,SIGNAL(destroyed(QObject*)),this,SLOT(detach()));
}
public
slots:
void detach(){
ctr--;
if(ctr<=0)delete this;
}
private:
int ctr;
};
In this code the detach() method will be called automatically when
the owning object is deleted. Unfortunately this does not take into
account that an owning object could call attach() twice by
accident.
在这段代码中detach()方法将会在所拥有的对象被删除的时候自动地被调用。不幸的是没有考虑到一个所拥有的对象会意外的调两次的attach()。
Owner Collection
所有者回收
A more intelligent approach is to not only remember how many
objects own this object, but which
ones.
更为智能的方法是不仅仅记住多少个对象拥有这个对象,而是哪个对象
class CountedObject:public QObject
{
public:
CountedObject(){}
void attach(QObject*o){
//check owner for validity
if(o==0)return;
//check for duplicates
if(owners.contains(o))return;
//register
owners.append(o);
connect(o,SIGNAL(destroyed(QObject*)),this,SLOT(detach(QObject*)));
}
public
slots:
void detach(QObject*o){
//remove it
owners.removeAll(o);
//remove self after last one
if(owners.size()==0)delete this;
}
private:
QListowners;
};
This code is finally able to protect itself against any faults
caused by called attach or detach multiple times. Unfortunately it
is not possible to protect the code against faults cause by not
calling attach at all, since it is not possible to force C++ into
telling the class how many pointers exist that point to any given
object.
这段代码最终能够在因为多次调用attach或者detach所导致的错误中保护自己了。不幸的是它不能够在根本没有调用attach的错误中保护自己,因为他没有办法强迫C++告诉类有多少指针存在指向给定的对象的指针。
Generic Approach
一般的方法
It is also possible to factor this code out into its own class, so
that multiple objects can be handled by the same garbage collector
instance and be delete simultaniously after the last parent was
deleted.
同样也可以将这段代码提取出来放入它自己的类中,以便多个对象能够通过相同的垃圾回收实例来处理,并且通过最后的父对象被删除的刺激而删除。
qgarbagecollector.cpp
qgarbagecollector.h
The code linked above can care about multiple child objects in an
independent garbage collector object. All children are deleted at
the same time when either the garbage collector itself or the last
parent (supervisor) is deleted. The garbage collector pointer does
not need to be carried around, since the instance will delete
itself upon completion of its task.
上面的代码链接能够在一个独立的垃圾回收的对象中处理多个子对象。当垃圾回收者或者最后的父对象被删除时,所有的子对象被同时删除。垃圾回收的指针不需要装载,实例在完成它的任务之后会删除它自己。