您的位置:首页 > 编程语言 > Qt开发

Qt的动态内存回收问题(1)

2016-09-26 10:53 405 查看
  Qt的动态内存回收机制容易让人觉得困惑,而能够参考的相关资料又很少,不少都是对此一笔带过,或者给出错误的说明。费了不少劲,了解到了一些皮毛。

前言:

程序开始时,最好先把指向动态内存的指针赋值为NULL,在delete这些变量的时候,也将指针赋值为NULL 。这样,可以在程序中,通过判断是否为NULL来判断是否已经分配空间。 

在delete过后,指针变量本身的状态是没有定义的。

在delete指针后立即将其赋值为NULL是经验之举。(在程序初始化(执行new之前)时,也建议将指针赋值为NULL。在new前,可以先判断指针是否为空再决定是不是new ——wulin)

对于一个不是由new返回的空指针实施delete操作会产生未定义的结果。

如果将delete操作实施与一个空指针,则不会产生任何动作也不会有错误。

1、一篇博文

http://www.cppblog.com/biao/archive/2009/07/02/89079.html

在这篇博文中,提到了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);
(在程序中测试过一次,该值默认值为false,需要通过setAttribute来置为true)

这可以用于非模态对话框,因为非模态对话框如果是用指针形式创建,但是再接着delete的话,窗口就没了,如果不使用delete释放窗口占用的资源,又会赞成泄漏。如果使用普通变量创建,同样的也因为变量作用域马上就结束而窗口也没了,另一种方法就是使用多线程,不过这个的代价大了点。所以这种技术在创建非模态对话框上是非常典型的运用。
测试方式:在Widget中分配大量的内存,显示与关闭多个此类窗口,看看任务管理器里此程序的内存变化情况,是否真正的释放了占用的内存(当然释放了)。在C++中使用new分配内存时,如array = new double[length],此时,给array的内存实际上并没有真正的分配,必须等到第一次使用这些内存后才会真正地为其分配物理内存,如:memset(array, 1, length * sizeof(double))
 

又一篇博文:
/*********************************************
http://topic.csdn.net/u/20101115/19/839436ff-8e51-4e89-bd75-ab452fb2a494.html
发表于:2010-11-15 19:32:20
class MainWindow;构造函数中增加:setAttribute(Qt::WA_DeleteOnClose)以后,

C/C++ code

 

MainWindow mainWindow;
mainWindow.setGeometry(30,30,1024,768);
mainWindow.show();
 

当关闭mainWindow时候,会有异常。改成:

C/C++ code

 
    

 MainWindow* mainWindow = new MainWindow;
    mainWindow->setGeometry(30, 30, 1024, 768);
 

    mainWindow->show();
 

这样才可以,原因就是前者定义在栈上,后者定义在堆上,所以当设置了WA_DeleteOnClose以后,调用MainWindow析构函数才不会异常抛出。
setAttribute(Qt::WA_DeleteOnClose) 什么原理,有没有人研究过源码?
**********************************************************************************************/
3. 窗口的内存管理交给父Widget:
Widget *w = new Widget(parent);
但这时,如果父Widget不结束,这个窗口的资源一直会占用着。
至于使用哪种技术来释放窗口的资源,要看具体的运用时,哪种方式更合适。
 
2、一本书
看到了《Foundations of Qt Development》,作者是Johan Thelin。这本书很适合使用过C++,开始接触Qt的程序员阅读。下面的内容摘自该书,有点涉嫌侵权。
Making C++ “Qt-er”

Because this is a book on programming, you will start with some code right away (see Listing 1-1).
让C++Qt化
Listing 1-1. A simple C++ class  一个简单的C++类

#include <string>

using std::string;

class MyClass

{

public:

MyClass( const string& text );

const string& text() const;

void setText( const string& text );

int getLengthOfText() const;

private:

string m_text;

};

Let’s make this class more powerful by using Qt.
Inheriting Qt

The first Qt-specific adjustment you will make to the code is really simple: you will simply let

your class inherit the QObject class, which will make it easier to manage instances of the class

dynamically by giving instances parents that are responsible for their deletion.
第一个Qt化的调整相当简单,让你的类继承QObject类就行了。这会使得对类的实例的动态管理更加简单。因为可以通过给实例父对象,这样就由父对象来负责删除了。

it is common—and recommended—to pass the parent as

an argument to the constructor as the first default argument to avoid having to type setParent

for each instance created from the class.
一个惯例,并且也是推荐的方式是,将父对象作为一个参数传给构造函数,成为第一个缺省参数。

Listing 1-2. Inheriting QObject and accepting a parent
继承QObject并接受父对象

#include <QObject>

#include <string>

using std::string;

class MyClass : public QObject

{

public:

MyClass( const string& text, QObject *parent = 0 );

...

};

Let’s look at the effects of the change, starting with Listing 1-3.It shows a main function

using the MyClass class dynamically without Qt.
下面我们看看变化。首先是不用Qt:
Listing 1-3. Dynamic memory without Qt
不用Qt使用动态内存

#include <iostream>

int main( int argc, char **argv )

{

MyClass *a, *b, *c;

a = new MyClass( "foo" );

b = new MyClass( "ba-a-ar" );

c = new MyClass( "baz" );

std::cout << a->text() << " (" << a->getLengthOfText() << ")" << std::endl;

a->setText( b->text() );

std::cout << a->text() << " (" << a->getLengthOfText() << ")" << std::endl;

int result = a->getLengthOfText() - c->getLengthOfText();

delete a;

delete b;

delete c;

return result;

}

Each new call must be followed by a call to delete to avoid a memory leak. Although it is

not a big issue when exiting from the main function (because most modern operating systems

free the memory when the application exits), the destructors are not called as expected. In

locations other than loop-less main functions, a leak eventually leads to a system crash when

the system runs out of free memory. Compare it with Listing 1-4, which uses a parent that is

automatically deleted when the main function exits. The parent is responsible for calling

delete for all children and—ta-da!—the memory is freed.
每个new都必须伴随一个delete来防止内存泄露。我们将这个例子和下一个比较。下面的1-4中,使用了一个父对象。
当main函数结束时,父对象会被自动删除。而这个父对象负责删除所有的孩子。哒-哒!内存被释放了。

■Note In the code shown in Listing 1-4, the parent object is added to show the concept. In real life, it

would be an object performing some sort of task—for example, a QApplication object, or (in the case of

a dialog box or a window) the this pointer of the window class.
注意,在下面的代码中,父对象是用来展示概念的。在实际应用中,它会是一个运行某种任务的对象,比如,QApplication对象,或者window类的this指针。
 
 Listing 1-4. Dynamic memory with Qt
使用Qt的动态内存使用

#include <QtDebug>

int main( int argc, char **argv )

{

QObject parent;

MyClass *a, *b, *c;

a = new MyClass( "foo", &parent );

b = new MyClass( "ba-a-ar", &parent );

c = new MyClass( "baz", &parent );

qDebug() << QString::fromStdString(a->text())

<< " (" << a->getLengthOfText() << ")";

a->setText( b->text() );

qDebug() << QString::fromStdString(a->text())

<< " (" << a->getLengthOfText() << ")";

return a->getLengthOfText() - c->getLengthOfText();

}

It might look odd to have a parent object like this, but most Qt applications
 use a QApplication object to actas a parent.
有这样一个父对象也许看起来有点怪怪的。但是,大多Qt应用程序使用一个QApplication对象来作为父对象。
When comparing the code complexity in Listing 1-3 and Listing 1-4, look at the different

memory situations, as shown in Figure 1-1. The parent is gray because it is allocated on the

stack and thus automatically deleted, whereas the instances of MyClass are white because they

are on the heap and must be handled manually. Because you use the parent to keep track of

the children, you trust the parent to delete them when it is being deleted. So you no longer

have to keep track of the dynamically allocated memory as long as the root object is on the

stack (or if you keep track of it).
由于父对象在栈空间里,所以就不用你来负责空间释放,这样,它的子对象占用的动态内存也就无需去手动释放了。




 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: