[z]Qt 内存管理机制
文章只是簡要的介紹了Qt的內存管理機制,對理解內存管理比較有幫助
?
強類型語言在創建對象時總會顯式或隱式地包含對象的類型信息。也就是說,強類型語言在分配對象內存空間時,總會關聯上對象的類型。相比之下,弱類型語言則不會這樣做。在分配了內存空間之后,有兩種方法釋放空間:手工釋放,或者是使用垃圾收集器。C++ 要求開發者手工釋放內存空間。這樣做的好處是,開發者對內存有完全的控制能力,知道什么時候釋放比較合適。Java 則使用垃圾收集器。它在后臺會有一個線程根據一定的算法不停地查看哪些對象已經不被使用,可以被回收。這樣做則可以將開發者從底層實現中解放出來,只需關注于業務邏輯。
本文關注于 Qt 的內存管理,這里會使用 Qt 的機制,來實現一個簡單的垃圾回收器。
?
C++ 內存管理機制
C++ 要求開發者自己管理內存。有三種策略:
最后一種通常成為“內存泄漏”,被認為是一種 bug。所以,我們現在就是要選出前面兩種哪一種更合適一些。有時候,delete 創建的對象要比 delete 它的所有子對象簡單得多;有時候,找出最后一個對象也是相當困難的。
?
Qt 內存管理機制
Qt 在內部能夠維護對象的層次結構。對于可視元素,這種層次結構就是子組件與父組件的關系;對于非可視元素,則是一個對象與另一個對象的從屬關系。在 Qt 中,刪除父對象會將其子對象一起刪除。這有助于減少 90% 的內存問題,形成一種類似垃圾回收的機制。
?
QPointer
QPointer 是一個模板類。它很類似一個普通的指針,不同之處在于,QPointer 可以監視動態分配空間的對象,并且在對象被 delete 的時候及時更新。
// QPointer 表現類似普通指針 QDate *mydate = new QDate(QDate::currentDate()); QPointer mypointer = mydata; mydate->year(); // -> 2005 mypointer->year(); // -> 2005// 當對象 delete 之后,QPointer 會有不同的表現 delete mydate;if(mydate == NULL)printf("clean pointer"); elseprintf("dangling pointer"); // 輸出 dangling pointerif(mypointer.isNull())printf("clean pointer"); elseprintf("dangling pointer"); // 輸出 clean 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);// 如果最后一個對象也被 delete,刪除自身if(owners.size() == 0)delete this;}private:QList owners; };現在我們的實現已經可以做到防止一個對象多次調用 attach() 和 detach() 了。然而,還有一個問題是,我們不能保證對象一定會調用 attach() 函數進行注冊。畢竟,這不是 C++ 內置機制。有一個解決方案是,重定義 new 運算符(這一實現同樣很復雜,不過可以避免出現有對象不調用 attach() 注冊的情況)。
?
原文:http://www.devbean.info/2011/03/qt_memory_management/
轉載于:https://www.cnblogs.com/waytofall/archive/2012/01/02/2309812.html
總結
以上是生活随笔為你收集整理的[z]Qt 内存管理机制的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 制作类似QQ截图软件
- 下一篇: 2012-01-10 自己写的基于jq