这篇文章首先发布于我的主页 http://www.devbean.info,以后也会直接发布在那里。现在有 Flex 4 的一篇和 《从 C++ 到 Objective-C》系列,感谢大家支持!
强类型语言在创建对象时总会显式或隐式地包含对象的类型信息。也就是说,强类型语言在分配对象内存空间时,总会关联上对象的类型。相比之下,弱类型 语言则不会这样做。在分配了内存空间之后,有两种方法释放空间:手工释放,或者是使用垃圾收集器。C++ 要求开发者手工释放内存空间。这样做的好处是,开发者对内存有完全的控制能力,知道什么时候释放比较合适。Java 则使用垃圾收集器。它在后台会有一个线程根据一定的算法不停地查看哪些对象已经不被使用,可以被回收。这样做则可以将开发者从底层实现中解放出来,只需关 注于业务逻辑。
本文关注于 Qt 的内存管理,这里会使用 Qt 的机制,来实现一个简单的垃圾回收器。
C++ 内存管理机制
C++ 要求开发者自己管理内存。有三种策略:
- 让创建的对象自己 delete 自己的子对象(这里所说的子对象,是指对象的属性,而不是子类,以下类似);
- 让最后一个对象处理 delete;
- 不管内存。
最后一种通常成为“内存泄漏”,被认为是一种 bug。所以,我们现在就是要选出前面两种哪一种更合适一些。有时候,delete 创建的对象要比 delete 它的所有子对象简单得多;有时候,找出最后一个对象也是相当困难的。
Qt 内存管理机制
Qt 在内部能够维护对象的层次结构。对于可视元素,这种层次结构就是子组件与父组件的关系;对于非可视元素,则是一个对象与另一个对象的从属关系。在 Qt 中,删除父对象会将其子对象一起删除。这有助于减少 90% 的内存问题,形成一种类似垃圾回收的机制。
QPointer
QPointer 是一个模板类。它很类似一个普通的指针,不同之处在于,QPointer 可以监视动态分配空间的对象,并且在对象被 delete 的时候及时更新。
-
- QDate *mydate = new QDate(QDate::currentDate());
- QPointer mypointer = mydata;
- mydate->year();
- mypointer->year();
-
-
- delete mydate;
-
- if(mydate == NULL)
- printf("clean pointer");
- else
- printf("dangling pointer");
-
-
- if(mypointer.isNull())
- printf("clean pointer");
- else
- printf("dangling pointer");
-
注意上面的代码。一个原始指针 delete 之后,其值不会被设置为 NULL,因此会成为野指针。但是,QPionter 没有这个问题。
QObjectCleanupHandler
Qt 对象清理器是实现自动垃圾回收的很重要的一部分。它可以注册很多子对象,并在自己删除的时候自动删除所有子对象。同时,它也可以识别出是否有子对象被删 除,从而将其从它的子对象列表中删除。这个类可以用于不在同一层次中的类的清理操作,例如,当按钮按下时需要关闭很多窗口,由于窗口的 parent 属性不可能设置为别的窗口的 button,此时使用这个类就会相当方便。
-
- QObjectCleanupHandler *cleaner = new QObjectCleanupHandler;
-
- QPushButton *w = new QPushButton("Remove Me");
- w->show();
-
- cleaner->add(w);
-
- connect(w, SIGNAL(clicked()), w, SLOT(deleteLater()));
-
- w = new QPushButton("Nothing");
- cleaner->add(w);
- w->show();
-
- w = new QPushButton("Remove All");
- cleaner->add(w);
- connect(w, SIGNAL(clicked()), cleaner, SLOT(deleteLater()));
- w->show();
在上面的代码中,创建了三个仅有一个按钮的窗口。第一个按钮点击后,会删除掉自己(通过 deleteLater() 槽),此时,cleaner 会自动将其从自己的列表中清除。第三个按钮点击后会删除 cleaner,这样做会同时删除掉所有未关闭的窗口。
Qt 垃圾收集
随着对象变得越来越复杂,很多地方都要使用这个对象的时候,什么时候作 delete 操作很难决定。好在 Qt 对所有继承自 QObject 的类都有很好的垃圾收集机制。垃圾收集有很多种实现方法,最简单的是引用计数,还有一种是保存所有对象。下面我们将详细讲解这两种实现方法。
引用计数
应用计数是最简单的垃圾回收实现:每创建一个对象,计数器加 1,每删除一个则减 1。
- class CountedObject
- {
- public:
- CountedObject()
- {
- ctr=0;
- }
-
- void attach()
- {
- ctr++;
- }
-
- void detach()
- {
- ctr--;
- if(ctr <= 0)
- delete this;
- }
- private:
- int ctr;
- };
每一个子对象在创建之后都应该调用 attach() 函数,使计数器加 1,删除的时候则应该调用 detach() 更新计数器。不过,这个类很原始,没有使用 Qt 方便的机制。下面我们给出一个 Qt 版本的实现:
- class CountedObject : public QObject
- {
- Q_OBJECT
- public:
- CountedObject()
- {
- ctr=0;
- }
-
- void attach(QObject *obj)
- {
- ctr++;
- connect(obj, SIGNAL(destroyed(QObject*)), SLOT(detach()));
- }
-
- public slots:
- void detach()
- {
- ctr--;
- if(ctr <= 0)
- delete this;
- }
-
- private:
- int ctr;
- };
我们利用 Qt 的信号槽机制,在对象销毁的时候自动减少计数器的值。但是,我们的实现并不能防止对象创建的时候调用了两次 attach()。
记录所有者
更合适的实现是,不仅仅记住有几个对象持有引用,而且要记住是哪些对象。例如:
- class CountedObject : public QObject
- {
- public:
- CountedObject()
- {
- }
-
- void attach(QObject *obj)
- {
-
- if(obj == 0)
- return;
-
- if(owners.contains(obj))
- return;
-
- owners.append(obj);
- connect(obj, SIGNAL(destroyed(QObject*)), SLOT(detach(QObject*)));
- }
-
- public slots:
- void detach(QObject *obj)
- {
-
- owners.removeAll(obj);
-
- if(owners.size() == 0)
- delete this;
- }
-
- private:
- QList owners;
- };
现在我们的实现已经可以做到防止一个对象多次调用 attach() 和 detach() 了。然而,还有一个问题是,我们不能保证对象一定会调用 attach() 函数进行注册。毕竟,这不是 C++ 内置机制。有一个解决方案是,重定义 new 运算符(这一实现同样很复杂,不过可以避免出现有对象不调用 attach() 注册的情况)。
本文来自 DevBean's World:http://www.devbean.info。
转载时请标明文章原始出处:http://www.devbean.info/2011/03/qt_memory_management/。
本文出自 “豆子空间” 博客,请务必保留此出处http://devbean.blog.51cto.com/448512/526734
相关推荐
在C++中,智能指针是管理动态分配对象的现代工具,它们负责自动释放内存,以防止内存泄漏。其中,`shared_ptr`是C++11引入的一种智能指针,它实现了引用计数机制,当最后一个`shared_ptr`实例被销毁时,其管理的对象...
qt内存管理 和回收的各种方式,例子简单明了。 还有比较详细的注释, 明白这些小例子 对于你理解qt的垃圾回收机制有不小的帮助。
在探讨QT内存管理机制及如何避免内存泄露之前,我们首先应当理解何为内存泄露。内存泄露是指程序中已分配的堆内存由于未能释放,导致一直占用这部分内存空间,最终可能导致应用程序运行缓慢甚至崩溃。在C++中,内存...
Qt内存管理的核心理念在于,由Qt自身来负责大部分对象的生命周期控制,包括内存的分配与释放。这种设计模式不仅简化了开发者的工作,减少了内存泄漏的风险,同时也提高了程序的稳定性和性能。 #### Qt对象的生命...
`QSharedPointer`是Qt提供的一种强大的内存管理工具,它简化了对象的生命周期管理,减少了内存泄漏的风险。在`VS2019 + Qt5.15`的环境中,理解和熟练使用`QSharedPointer`对于编写高效、可靠的Qt应用程序至关重要。...
在IT领域,Qt是一个非常流行的跨平台应用开发框架,它由Qt公司开发并维护,支持Windows、Linux、macOS、Android以及iOS等多...对于Qt开发者来说,这是一个很好的实践项目,可以帮助他们深入理解Qt框架和系统资源管理。
在开发Qt应用程序时,内存管理是一项关键任务,正确地分配和释放内存对于程序的稳定性和性能至关重要。...正确使用VLD,结合Qt的内存管理机制,可以提高代码质量,避免因内存泄漏导致的运行时错误和性能下降。
例如,`/proc/<pid>/status`包含了进程的状态信息,`/proc/<pid>/statm`提供了内存使用情况。这些数据可以定期刷新并显示在QTableWidget中,更新频率可以通过定时器来控制。 对于交互功能,我们需要处理按钮点击...
在Qt中,信号与槽机制是实现对象间通信的一种方式。信号(signal)由对象在特定事件发生时发出,而槽(slot)则是可以接收这些信号的对象成员函数。本例演示如何使用信号与槽来响应按钮点击事件。 ```cpp 1#include ...
QNetworkAccessManager是QT中的核心网络组件,它负责管理所有的网络请求。你可以将其看作一个工厂,当需要从网络获取数据时,你可以向它发出请求,它会返回一个QNetworkReply对象,该对象将处理实际的网络通信。...
在Qt4程序中,通常不需要自己管理内存释放等问题,因为Qt4提供了相应的对象生命周期管理机制。 3. QApplication类 - QApplication类管理GUI程序的控制流和主要设置。它应该在程序的main函数中创建,以确保应用程序...
QT内存泄漏是一个常见的编程问题,尤其是在开发大型、复杂的软件应用时。QT框架提供了一个强大的C++库,使得创建跨平台的图形用户界面变得容易。然而,如果没有正确地管理内存,程序可能会消耗过多资源,导致性能...
QObject 有三个主要职责:内存管理、内省和事件处理制。 事件的分发: Qt 消息处理机制使用 QCoreApplication 或 QApplication 来负责将事件分发给相应的 QObject 对象。在非 GUI 程序中,QCoreApplication 负责将...
通过在字符串中嵌入HTML标签,例如<b>(粗体)、<i>(斜体)、<font>(字体)、<h1>到<h6>(标题)等,可以实现富文本的显示效果。这对于设计具有丰富文本格式的GUI应用程序非常有帮助。 10. 编译和运行Qt程序 编写...
这样做可以让我们利用Qt的信号和槽机制,以及自动内存管理。 2. **打开日志文件**: 在`LogFile`类中,我们需要一个方法来打开或创建日志文件。这通常在类的构造函数中完成,使用QFile的`open()`函数,指定写入模式...
本篇将深入探讨Qt库中的内存管理机制,以及如何利用Qt API实现高效的内存共享。 首先,我们要了解Qt的内存管理策略。Qt采用智能指针(如QSharedPointer和QWeakPointer)来帮助开发者管理和控制对象的生命周期。这些...
共享内存是一种通信机制,它允许多个进程访问同一块内存区域,从而实现数据的快速交换。在QT中,我们可以利用QSharedMemory类来实现这一功能。以下是对这一技术的详细解释。 首先,我们需要了解QT中的QImage类。...
在IT行业中,内存管理和线程调用是两个关键的领域,尤其在C++这样的系统级编程语言中。本文将深入探讨“qt下写的内存池和线程调用”这一主题,尽管它与Qt库本身的使用可能关系不大,但Qt作为一款强大的C++图形用户...
#include <QApplication> #include <QWidget> #include <QPushButton> #include <QLabel> #include <QImage> #include "opencv2/opencv.hpp" // 声明槽函数 void onButtonClicked(); int main(int argc, char *...