2016-06-15 19 views
5

我剛剛提供了this問題的答案,並且希望提供一個工作示例,當我注意到新創建的QMimeData實例返回的QListModel::mimeData()在應用程序終止之前不會被刪除。Qt5中的內存泄漏?如何讓QMimeData被刪除?

所以這不是一個真正的內存泄漏,因爲Qt的處理上關閉所有QMimeData實例,但你只需要拖動&滴足夠長的時間,把正確的內容爲您的MIME數據,讓內存運行完整。

我錯過了什麼嗎?有沒有辦法告訴Qt在不再需要時立即刪除QMimeData實例?

請注意:

我知道的QMimeData每個實例得到由Qt的程序終止自動刪除。 這裏的問題並不是真正的內存泄漏,如valgrindcppcheck報告的,但它看起來像多個並且可能非常大的QMimeData實例在運行時不會被清除,這也會消耗內存。

示例代碼:

#include <QtWidgets> 
#include <iostream> 

struct TrackedMimeData : public QMimeData { 
    TrackedMimeData(const QString & text) { 
     std::cout << this << std::endl; 
     setText(text); 
    } 
    ~TrackedMimeData() { 
     std::cout << "~" << this << std::endl; 
    } 
}; 

struct MyListWidget : QListWidget { 
    MyListWidget() { 
     setDragEnabled(true); 
     addItem("item1"); 
     addItem("item2"); 
    } 
    QMimeData * mimeData(const QList<QListWidgetItem *>) const override { 
     return new TrackedMimeData("hello"); 
    } 
}; 

int main(int argsc, char *argsv[]) { 
    QApplication application(argsc, argsv); 
    MyListWidget gui; 
    gui.show(); 
    return application.exec(); 
} 

輸出示例如下:

0xa58750 
0xa4e0f0 
~0xa4e0f0 
0xa3c6c0 
~0xa3c6c0 
0xa51880 
0xa5ecd0 
0xa31f50 
0xa57db0 
0xa5afc0 
~0xa5afc0 
0xa5aa70 
~0xa5aa70 
------ CLOSE WINDOW 
~0xa58750 
~0xa51880 
~0xa5ecd0 
~0xa31f50 
~0xa57db0 

的析構函數被調用收盤前僅在降獲取的受理申請。

Btw。我在本地Qt 5.6 @ 1fcdb6cafcf - 在一臺計算機上,並在5.6.0-19.fc23 Fedora 23預編譯在另一臺計算機上。所以我懷疑這只是一個暫時的發展狀態。

+0

'QMimeData'繼承'QObject'並在內部將其數據存儲爲字符串的'QVector'和'QVariant'對。它具有與其他'QObject'派生類體驗相同的內存管理限制。 –

+0

'cppcheck'在本例中找不到任何東西,因爲'QMimeData'實例在最後被刪除。但是在那之前,我可能已經在成千上萬個新的'QMimeData'實例中存儲了千兆字節的數據 - 這是我承認的,而不是傳統的內存泄漏。 – frans

+1

您是否真的看到過度的內存消耗,或者只是擔心會發生? –

回答

2

只有在忘記刪除由mimeData()返回的指針時纔會發生內存泄漏。您必須像使用任何指針一樣管理所有權。

例如,如果您使用setMimeData()mimeData()返回的指針傳遞給QDrag對象,他將獲得它的所有權,並且將在拖動操作結束時負責刪除它。在這種情況下沒有內存泄漏。

參見:http://doc.qt.io/qt-5/qdrag.html#setMimeData

+0

我使用從'QAbstractItemView'派生的'QListWidget'並創建/處理'QDrag'對象。我只創建'QMimeData',然後由'QDrag'對象擁有,我無法訪問它。 – frans

+1

是的,但文檔說QDrag :: setMimeData將所有權傳遞給QDrag對象。這意味着當刪除QDrag時,QMimeData對象將被刪除 – galinette

+0

好了,但是'QDrag'對象沒有及時刪除 - 我會在我的問題中更正這個細節,但問題是一樣的。 – frans

1

應該發生的事情,如果不是,那麼它是一個錯誤 - 沒有什麼可以做這件事,不是報告它,如果它尚未報道等。

我不能在OS X上用Qt 5.6重現它。它可能是特定於平臺的,或者是舊版Qt中已經修復的bug。只要在拖動結束時釋放鼠標按鈕,mime數據就會被刪除。執行析構函數時的調用堆棧具有從事件循環通過deleteLater某處調用的QDrag析構函數。我已經逐字使用了你的代碼。

補充工具欄:如果你真的希望儘可能短,可以將它做得更小一些。這是我得到的,雖然我同意它主要是分裂頭髮。你的問題通過提供了一個工作示例,勝過99%的其他問題 - 對此很讚賞!我認爲重要的事情是:

  1. 包含整個所需的Qt模塊。
  2. 使用qDebug而不是std::cout &al,它保存了一個包含並且是更多的Qt風格。
  3. 無論如何您使用struct訪問說明符並不重要。
  4. 一般來說,析構函數在公共基類中是虛擬的,或者它們永遠不會屈服於切片。所以你不必拼出來。

我有一個示例代碼,當然我沒有遵循這個。我喜歡把它稱爲遺留代碼:)

#include <QtWidgets> 

struct TrackedMimeData : QMimeData { 
    TrackedMimeData(const QString & text) { 
     qDebug() << this; 
     setText(text); 
    } 
    ~TrackedMimeData() { 
     qDebug() << "~" << this; 
    } 
}; 

struct MyListWidget : QListWidget { 
    MyListWidget() { 
     setDragEnabled(true); 
     addItem("item1"); 
     addItem("item2"); 
    } 
    QMimeData * mimeData(const QList<QListWidgetItem *>) const override { 
     return new TrackedMimeData("hello"); 
    } 
}; 

int main(int argsc, char *argsv[]) { 
    QApplication application(argsc, argsv); 
    MyListWidget gui; 
    gui.show(); 
    return application.exec(); 
} 
+0

不幸的是我沒有看到qDebug的任何輸出 - 無論是在終端上還是在QtCreator中。但用'cout'輸出證明析構函數沒有被調用(在我的情況下) – frans

+0

@frans奇怪的是...什麼平臺和Qt版本是在?這絕對是一個錯誤。請報告是否已經報告。 –

+0

返回假期我已經創建了https://bugreports.qt.io/browse/QTBUG-54667 – frans