轉載於:envenler的Qt內存泄漏問題

今天對於自己寫的Qt程序在內存泄漏上起了很大懷疑,懷疑自己的程序中很多地方存在泄漏。然後就找了一下資料,自己寫了個小程序和大家分享一下。

首先我們知道Qt有一套回收內存的機制,下面是網摘的規則,如下:

1、所有繼承自QOBJECT類的類,如果在new的時候指定了父親,那麼它的清理時在父親被delete的時候delete的,所以如果一個程序中,所有的QOBJECT類都指定了父親,那麼他們是會一級級的在最上面的父親清理時被清理,而不用自己清理;

2、程序通常最上層會有一個根的QOBJECT,就是放在setCentralWidget()中的那個QOBJECT,這個QOBJECT在 new的時候不必指定它的父親,因爲這個語句將設定它的父親爲總的QAPPLICATION,當整個QAPPLICATION沒有時它就自動清理,所以也無需清理。9這裏QT4和QT3有不同,QT3中用的是setmainwidget函數,但是這個函數不作爲裏面QOBJECT的父親,所以QT3中這個頂層的QOBJECT要自行銷燬)。

3、這是有人可能會問那如果我自行delete掉這些QT接管負責銷燬的指針了會出現什麼情況呢,如果時這樣的話,正常情況下QT的擁有這個對象的那個父親會知道這件事情,它會直到它的兒子被你直接DELETE了,這樣它會將這個兒子移出它的列表,並且重新構建顯示內容,但是直接這樣做時有風險的!也就是要說的下一條

4、當一個QOBJECT正在接受事件隊列時如果中途被你DELETE掉了,就是出現問題了,所以QT中建議大家不要直接DELETE掉一個 QOBJECT,如果一定要這樣做,要使用QOBJECT的deleteLater()函數,它會讓所有事件都發送完一切處理好後馬上清除這片內存,而且就算調用多次的deletelater也不會有問題。

5、QT不建議在一個QOBJECT 的父親的範圍之外持有對這個QOBJECT的指針,因爲如果這樣外面的指針很可能不會察覺這個QOBJECT被釋放,會出現錯誤,如果一定要這樣,就要記住你在哪這樣做了,然後抓住那個被你違規使用的QOBJECT的destroyed()信號,當它沒有時趕快置零你的外部指針。當然我認爲這樣做是及其麻煩也不符合高效率編程規範的,所以如果要這樣在外部持有QOBJECT的指針,建議使用引用或者用智能指針,如QT就提供了智能指針針對這些情況,見最後一條。

6、QT中的智能指針封裝爲QPointer類,所有QOBJECT的子類都可以用這個智能指針來包裝,很多用法與普通指針一樣,可以詳見QT assistant

通過調查這個QT內存管理功能,發現了很多東西,現在覺得雖然這個QT弄的有點小複雜,但是使用起來還是很方便的,最後要說的是某些內存泄露的檢測工具會認爲QT的程序因爲這種方式存在內存泄露,發現時大可不必理會。

除了這些情況,我程序中還有一個情況,我在函數中new出來的對象,父節點設置爲this時,當每次調用函數時,這個對象是怎麼釋放的。我假設了兩種情況:1.函數調用完之後,不管父窗體(this)是否銷燬都會釋放內存;2.函數調用完不釋放內存,每調用一次分配一次,到最後父窗體銷燬時,所有new出來的對象一起釋放。下面我舉了個例子來觀察,如下:

  1. void MainWindow::new_test()  
  2. {  
  3.     QLabel *label = new QLabel("label",this);  
  4.   
  5.     qDebug() << label << endl;  
  6.     label->deleteLater();  
  7. }  

一個button的槽連接:

  1. connect(btn,SIGNAL(clicked()),this,SLOT(new_test()));  

 

首先我們測試手動釋放new出來的label,qDebug的輸出如下:

  1. QLabel(0x9f5bb8)   
  2.   
  3. QLabel(0x9f5bb8)   
  4.   
  5. QLabel(0x9f5bb8)   
  6.   
  7. QLabel(0x9f5bb8)   
  8.   
  9. QLabel(0x9f5bb8)   

說明釋放成功,但是還不明確是否是函數調用完釋放的還是調用delete釋放的,接下來測試一下把調用delete釋放對象的那行代碼註釋掉,繼續觀察:

qDebug的輸出爲:

  1. QLabel(0x9f5bb8)   
  2.   
  3. QLabel(0x10b9af8)   
  4.   
  5. QLabel(0x10b59f0)   
  6.   
  7. QLabel(0x9fe310)   
  8.   
  9. QLabel(0x10b6e40)   


這就說明函數裏面new出來的對象在函數調用完後不會自動釋放,每次都重新分配了內存,這些內存都是在父窗體(this)銷燬前銷燬的,如果程序一直運行這個,毋庸置疑肯定會死機。

最後還是要根據你創建對象的作用範圍來決定具體在哪裏銷燬好。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章